Mic*_*tum 1 .net c# architecture multithreading
我想编写我的第一个真正的MultiThreaded C#应用程序.虽然之前我使用过BackgroundWorker并且知道关于lock(对象)的一两件事,但我从未使用Thread对象,Monitor.Enter等等,而我完全迷失了从哪里开始设计架构.
基本上我的程序在后台运行.每隔5分钟,它会检查一个Web服务.如果Web服务返回数据,它将从此数据中创建作业并将其传递给JobQueue.JobQueue然后按顺序处理这些作业 - 如果添加新作业但仍在处理作业时,它将对作业进行排队.此外,还有一个Web服务器允许远程访问该程序.
我看到它的方式,我需要4个线程:
程序启动时应创建线程2-4,程序结束时应该结束,因此它们只运行一次.
如上所述,我真的不知道架构将如何工作.线程1会做什么?当MyProgram类被实例化时,它是否应该具有Queue<Job>属性?我该如何开始我的线程?据我所知,我需要将一个函数传递给线程 - 该函数应该放在哪里?如果我有一个类"MyJobQueueThreadClass"具有线程3的所有函数,那么如何访问MyProgram类上的Object?如果一个线程只是一个函数,我该如何阻止它提前结束?如上所述,线程2等待5分钟,然后执行一系列功能,并一遍又一遍地重启5分钟计时器(Thread.Sleep(300)?),直到我的程序结束(调用Thread.Abort(Thread2)in MyProgram的关闭/退出/解析器?)
dtb*_*dtb 16
让我们一步一步地完成它:
class Program {
Run Code Online (Sandbox Code Playgroud)
作业队列是一种数据结构:
private static Queue<Job> jobQueue;
Run Code Online (Sandbox Code Playgroud)
如果多个线程访问此数据结构,则需要将其锁定:
private static void EnqueueJob(Job job) {
lock (jobQueue) {
jobQueue.Enqueue(job);
}
}
private static Job DequeueJob() {
lock (jobQueue) {
return jobQueue.Dequeue();
}
}
Run Code Online (Sandbox Code Playgroud)
让我们添加一个从Web服务检索作业并将其添加到队列的方法:
private static void RetrieveJob(object unused) {
Job job = ... // retrieve job from webservice
EnqueueJob(job);
}
Run Code Online (Sandbox Code Playgroud)
以及在循环中处理队列中的作业的方法:
private static void ProcessJobs() {
while (true) {
Job job = DequeueJob();
// process job
}
}
Run Code Online (Sandbox Code Playgroud)
我们来运行这个程序:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
如果您运行该程序,您会注意到每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();
}
}
Run Code Online (Sandbox Code Playgroud)
如果你再次运行程序,它不会抛出异常,一切都应该正常.但是你会注意到你必须杀死进程因为ProcessJobs-thread永远不会结束.那么,你如何结束你的计划?
我建议您定义一个特殊作业,指示作业处理的结束:
private static void ProcessJobs() {
while (true) {
Job job = DequeueJob();
if (job == null) {
break;
}
// process job
}
// when ProcessJobs returns, the thread ends
}
Run Code Online (Sandbox Code Playgroud)
然后停止计时器并将特殊作业添加到作业队列:
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();
}
Run Code Online (Sandbox Code Playgroud)
我希望这隐含地回答你所有的问题:-)
通过指定可访问所有必需数据结构的方法来启动线程
从多个线程访问数据结构时,需要锁定数据结构
lock声明都可以当多个线程相互依赖时(例如,等待另一个线程完成任务的线程)使用信号
不要使用Thread.Abort,Thread.Interrupt,Thread.Resume,Thread.Sleep,Thread.Suspend,Monitor.Pulse,Monitor.Wait
| 归档时间: |
|
| 查看次数: |
3376 次 |
| 最近记录: |