gpt4 book ai didi

c# - C#/.net 3.5SP1 中线程的基本架构和生命周期

转载 作者:太空宇宙 更新时间:2023-11-03 17:11:44 25 4
gpt4 key购买 nike

我想编写我的第一个真正的多线程 C# 应用程序。虽然我之前使用过 BackgroundWorker 并且对 lock(object) 了解一两件事,但我从未使用过 Thread 对象、Monitor.Enter 等,而且我完全不知道从哪里开始设计架构。

基本上我的程序在后台运行。每 5 分钟,它检查一次 Web 服务。如果 Web 服务返回数据,它会根据该数据创建作业并将其传递到 JobQueue 中。 JobQueue 然后按顺序处理这些作业——如果在它仍在处理一个作业时添加了一个新作业,它会将作业排队。此外,还有一个 Web 服务器允许远程访问该程序。

在我看来,我需要 4 个线程:

  1. 主线程
  2. “5 分钟计时器”和 Web 服务线程
  3. 作业队列
  4. 网络服务器

线程 2-4 应在程序启动时创建并在程序结束时结束,因此它们只运行一次。

如前所述,我真的不知道该架构将如何工作。线程 1 会做什么?当 MyProgram 类被实例化时,它是否应该有一个 Queue<Job>作为属性(property)?我将如何开始我的线程?据我所知,我需要将一个函数传递给线程——该函数应该放在哪里?如果我有一个类“MyJobQueueThreadClass”,它具有线程 3 的所有功能,那将如何访问 MyProgram 类上的对象?如果线程只是一个函数,我该如何防止它提前结束?如前所述,线程2等待5分钟,然后执行一系列功能,并一遍又一遍地重新启动5分钟计时器(Thread.Sleep(300)?),直到我的程序结束(在Call Thread.Abort(Thread2)中MyProgram 的关闭/退出/析构函数?)

最佳答案

让我们一步一步来:

1.

class Program {

作业队列是一种数据结构:

    private static Queue<Job> jobQueue;

如果这个数据结构被多个线程访问,你需要锁定它:

    private static void EnqueueJob(Job job) {
lock (jobQueue) {
jobQueue.Enqueue(job);
}
}

private static Job DequeueJob() {
lock (jobQueue) {
return jobQueue.Dequeue();
}
}

让我们添加一个从 Web 服务检索作业并将其添加到队列的方法:

    private static void RetrieveJob(object unused) {
Job job = ... // retrieve job from webservice
EnqueueJob(job);
}

以及循环处理队列中作业的方法:

    private static void ProcessJobs() {
while (true) {
Job job = DequeueJob();
// process job
}
}

让我们运行这个程序:

    private static void Main() {
// run RetrieveJob every 5 minutes using a timer
Timer timer = new Timer(RetrieveJob);
timer.Change(TimeSpan.FromMinutes(0), TimeSpan.FromMinutes(5));

// run ProcessJobs in thread
Thread thread = new Thread(ProcessJobs);
thread.Start();

// block main thread
Console.ReadLine();
}
}

2.

如果您运行该程序,您会注意到每 5 分钟添加一个作业。但是 jobQueue.Dequeue() 将抛出 InvalidOperationException,因为在检索到作业之前作业队列是空的。

为了解决这个问题,我们使用信号量将作业队列变成阻塞队列:

    private static Semaphore semaphore = new Semaphore(0, int.MaxValue);

private static void EnqueueJob(Job job) {
lock (jobQueue) {
jobQueue.Enqueue(job);
}
// signal availability of job
semaphore.Release(1);
}

private static Job DequeueJob() {
// wait until job is available
semaphore.WaitOne();
lock (jobQueue) {
return jobQueue.Dequeue();
}
}

3.

如果您再次运行该程序,它不会抛出异常并且一切正常。但是您会注意到您必须终止进程,因为 ProcessJobs 线程永远不会结束。那么,您如何结束程序?

我建议你定义一个特殊的作业来指示作业处理的结束:

    private static void ProcessJobs() {
while (true) {
Job job = DequeueJob();
if (job == null) {
break;
}
// process job
}
// when ProcessJobs returns, the thread ends
}

然后停止计时器并将特殊作业添加到作业队列中:

    private static void Main() {
// run RetrieveJob every 5 minutes using a timer
Timer timer = new Timer(RetrieveJob);
timer.Change(TimeSpan.FromMinutes(0), TimeSpan.FromMinutes(5));

// run ProcessJobs in thread
Thread thread = new Thread(ProcessJobs);
thread.Start();

// block main thread
Console.ReadLine();

// stop the timer
timer.Change(Timeout.Infinite, Timeout.Infinite);

// add 'null' job and wait until ProcessJobs has finished
EnqueueJob(null);
thread.Join();
}

我希望这隐含地回答了你所有的问题:-)

经验法则

  • 通过指定一个可以访问所有必要数据结构的方法来启动一个线程

  • 当从多个线程访问数据结构时,您需要锁定数据结构

    • 在大多数情况下,lock 语句就可以了
    • 使用 ReaderWriterLockSlim如果有许多线程从不经常更改的数据结构中读取数据。
    • 如果数据结构是不可变的,则不需要锁。
  • 当多个线程相互依赖时(例如,一个线程等待另一个线程完成任务)使用信号

  • 不要使用 Thread.Abort、Thread.Interrupt、Thread.Resume、Thread.Sleep、Thread.Suspend、Monitor.Pulse、Monitor.Wait

关于c# - C#/.net 3.5SP1 中线程的基本架构和生命周期,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2025699/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com