我想编写我的第一个真正的MultiThreaded C#应用程序。虽然我以前使用过BackgroundWorker,并且知道关于锁(对象)的一两件事,但我从来没有使用过线程对象、Monitor.Enter等等,我完全不知道从哪里开始设计体系结构。
基本上我的程序是在后台运行的。它每5分钟检查一次web服务。如果web服务返回数据,它将利用这些数据创建乔布斯并将其传递给JobQueue。然后,JobQueue依次对这些作业工作--如果在仍在处理一个作业时添加了一个新作业,它将对作业排队。此外,还有一个允许远程访问程序的Web服务器。
在我看来,我需要四根线:
线程2-4应该在程序启动时创建,在程序结束时结束,因此它们只运行一次。
如前所述,我真的不知道这个体系结构会如何运作。线程1会做什么?当MyProgram类被实例化时,它是否应该有一个Queue<Job>作为属性?我该怎么开始我的丝线?据我所见,我需要把一个函数传递到线程中--这个函数应该放在哪里?如果我有一个"MyJobQueueThreadClass“类,它拥有线程3的所有函数,那么如何访问MyProgram类上的对象?如果线程只是一个函数,我如何防止它提前结束呢?如前所述,线程2等待5分钟,然后执行一系列函数,并重新启动5分钟计时器(Thread.Sleep(300)?)一次又一次,直到我的程序结束(在关闭/退出/析构函数中调用Thread.Abort(Thread2)?)
发布于 2010-01-08 06:01:23
让我们一步一步地看一遍:
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();
}我希望这能含蓄地回答你所有的问题:-)
经验法则
- Use [ThreadPool.QueueUserWorkItem](http://msdn.microsoft.com/en-us/library/system.threading.threadpool.queueuserworkitem.aspx) for small tasks
- Use a [Timer](http://msdn.microsoft.com/en-us/library/system.threading.timer.aspx) for small, periodic tasks
- Use a [Thread](http://msdn.microsoft.com/en-us/library/system.threading.thread.aspx) for long-running tasks
- In most cases the `lock` statement will do
- Use a [ReaderWriterLockSlim](http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim.aspx) if there are many threads reading from a data structure that is infrequently changed.
- You don't need a lock if the data structure is immutable.
- [ManualResetEvent](http://msdn.microsoft.com/en-us/library/system.threading.manualresetevent.aspx), [AutoResetEvent](http://msdn.microsoft.com/en-us/library/system.threading.autoresetevent.aspx), [Semaphore](http://msdn.microsoft.com/en-us/library/system.threading.semaphore.aspx)
- [Thread.Join](http://msdn.microsoft.com/en-us/library/system.threading.thread.join.aspx) if the task is waiting for the thread to end
发布于 2010-01-08 05:54:33
在生命周期方面,您不必担心主程序线程--它已经超出了您的控制范围。
您可以在主线程上设置一个timer对象(System.Threading.Timer),它每隔5分钟就会过去一次--来自.NET线程池的一个线程将被用来调用您经过的事件处理程序。
我会使用这个线程连接到web服务,下载作业数据,并将作业推入作业队列,因为它是一个工作单元。一旦您完成了线程的工作,.NET将自动将其放回池中。定时器将继续发送重复此过程的经过事件。到目前为止,您实际上还不需要执行任何显式线程处理,这通常是一件好事!
然后,您需要一个从队列中弹出作业并对其进行处理的线程--您可以将其实现为一个使用工作线程模式封装Thread实例的类。它的功能是从作业队列中弹出作业并对其进行处理,并在工作完成后进入睡眠一段时间(队列为空)。当线程醒来时,它将继续循环,或者直到主线程发出停止的信号。
您也可以使用BackgroundWorker来完成这个任务,但是如果您想学习多线程,那么第一个选项将让您更深入地了解Thread。
这种模式是相当普遍的,通常被称为生产者-消费者,你肯定可以谷歌的例子。这里的主要复杂性在于同步访问队列,因为它是生产者线程和使用者线程之间共享的,您不希望它们踩到对方的脚尖。
https://stackoverflow.com/questions/2025699
复制相似问题