- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我想编写我的第一个真正的多线程 C# 应用程序。虽然我之前使用过 BackgroundWorker 并且对 lock(object) 了解一两件事,但我从未使用过 Thread 对象、Monitor.Enter 等,而且我完全不知道从哪里开始设计架构。
基本上我的程序在后台运行。每 5 分钟,它检查一次 Web 服务。如果 Web 服务返回数据,它会根据该数据创建作业并将其传递到 JobQueue 中。 JobQueue 然后按顺序处理这些作业——如果在它仍在处理一个作业时添加了一个新作业,它会将作业排队。此外,还有一个 Web 服务器允许远程访问该程序。
在我看来,我需要 4 个线程:
线程 2-4 应在程序启动时创建并在程序结束时结束,因此它们只运行一次。
如前所述,我真的不知道该架构将如何工作。线程 1 会做什么?当 MyProgram 类被实例化时,它是否应该有一个 Queue<Job>
作为属性(property)?我将如何开始我的线程?据我所知,我需要将一个函数传递给线程——该函数应该放在哪里?如果我有一个类“MyJobQueueThreadClass”,它具有线程 3 的所有功能,那将如何访问 MyProgram 类上的对象?如果线程只是一个函数,我该如何防止它提前结束?如前所述,线程2等待5分钟,然后执行一系列功能,并一遍又一遍地重新启动5分钟计时器(Thread.Sleep(300)?),直到我的程序结束(在Call Thread.Abort(Thread2)中MyProgram 的关闭/退出/析构函数?)
最佳答案
让我们一步一步来:
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();
}
}
如果您运行该程序,您会注意到每 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();
}
}
如果您再次运行该程序,它不会抛出异常并且一切正常。但是您会注意到您必须终止进程,因为 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
语句就可以了当多个线程相互依赖时(例如,一个线程等待另一个线程完成任务)使用信号
不要使用 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/
我正在开发一个使用多个 turtle 的滚动游戏。玩家 turtle 根据按键命令在 Y 轴上移动。当危害和好处在 X 轴上移动时,然后循环并改变 Y 轴位置。我尝试定义一个名为 colliding(
我不明白为什么他们不接受这个作为解决方案,他们说这是一个错误的答案:- #include int main(void) { int val=0; printf("Input:- \n
我正在使用基于表单的身份验证。 我有一个注销链接,如下所示: 以及对应的注销方法: public String logout() { FacesContext.getCurren
在 IIS7 应用程序池中有一个设置 Idle-time out 默认是 20 分钟,其中说: Amount of time(in minutes) a worker process will rem
我是一名优秀的程序员,十分优秀!