我有一个需要大量初始化的对象(在强壮的机器上1-2秒)。虽然一旦初始化,它只需要大约20毫秒来完成一个典型的“工作”
为了防止每次应用想要使用它时重新初始化它(在典型的使用情况下可能是每秒50次或完全没有),我决定给它一个工作,并且让它在自己的线程上运行,检查在que中是否有任何工作。但是,我并不完全确定如何制作一个无论是否有工作而无条件运行的线程。
这是我到目前为止所做的,欢迎任何批评
private void DoWork()
{
while (true)
{
if (JobQue.Count > 0)
{
// do work on JobQue.Dequeue()
}
else
{
System.Threading.Thread.Sleep(50);
}
}
}
经过深思熟虑:我以为我可能需要优先杀死这个线程让它永远运行,所以我想我会添加一个告诉线程结束的Job类型。关于如何结束这样的线程的任何想法也表示赞赏。
答案 0 :(得分:19)
你需要至lock
,所以你可以Wait
和Pulse
:
while(true) {
SomeType item;
lock(queue) {
while(queue.Count == 0) {
Monitor.Wait(queue); // releases lock, waits for a Pulse,
// and re-acquires the lock
}
item = queue.Dequeue(); // we have the lock, and there's data
}
// process item **outside** of the lock
}
添加如:
lock(queue) {
queue.Enqueue(item);
// if the queue was empty, the worker may be waiting - wake it up
if(queue.Count == 1) { Monitor.PulseAll(queue); }
}
您可能还想查看this question,这会限制队列的大小(如果它太满则会阻塞)。
答案 1 :(得分:2)
您需要一个同步原语,如WaitHandle(查看静态方法)。通过这种方式,您可以“发出信号”工作线程有效。它会检查队列并继续工作,直到队列为空,此时它会等待互斥锁再次发出信号。
使其中一个作业项也是一个退出命令,这样你可以在退出线程时发出工作线程的信号
答案 2 :(得分:1)
在大多数情况下,我这样做与您的设置非常相似 - 但不是使用相同的语言。我有一个使用数据结构(在Python中)的优势,它将阻塞线程直到项目被放入队列,从而无需进行睡眠调用。
如果.NET提供类似的类,我会考虑使用它。线程阻塞比在休眠调用中旋转的线程要好得多。
你可以传递的工作可以像“null”一样简单;如果代码收到一个空值,它知道是时候突破并回家了。
答案 3 :(得分:1)
抓住Parallel Framework。它有BlockingCollection<T>,您可以将其用作作业队列。你如何使用它是:
线程将被阻止,直到项目出现在集合中。无论谁转过它都会得到它(取决于CPU)。我现在正在使用它,效果很好。
它还有一个优势,就是依靠MS来编写特别讨厌的代码,其中多个线程访问相同的资源。无论什么时候你都可以让其他人写下你应该去的。当然,假设他们拥有比您更多的技术/测试资源和综合经验。
答案 4 :(得分:1)
如果你真的不需要让线程退出(并且只是希望它不让你的应用程序继续运行),你可以将Thread.IsBackground设置为true,它将在所有非后台线程结束时结束。 Will和Marc都有很好的解决方案来处理队列。
答案 5 :(得分:1)
我已经实现了一个后台任务队列而没有使用任何类型的while
循环,或者是脉冲,或者等待,或者实际上是在触摸Thread
个对象。它似乎工作。 (我的意思是它在生产环境中过去18个月每天处理数千个任务而没有任何意外行为。)这是一个有两个重要属性的类,Queue<Task>
和BackgroundWorker
。有三种重要方法,缩写如下:
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
if (TaskQueue.Count > 0)
{
TaskQueue[0].Execute();
}
}
private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Task t = TaskQueue[0];
lock (TaskQueue)
{
TaskQueue.Remove(t);
}
if (TaskQueue.Count > 0 && !BackgroundWorker.IsBusy)
{
BackgroundWorker.RunWorkerAsync();
}
}
public void Enqueue(Task t)
{
lock (TaskQueue)
{
TaskQueue.Add(t);
}
if (!BackgroundWorker.IsBusy)
{
BackgroundWorker.RunWorkerAsync();
}
}
并不是没有等待和脉动。但这一切都发生在BackgroundWorker
内。这只是在队列中删除任务时才会唤醒,运行直到队列为空,然后再回到睡眠状态。
我远不是线程专家。如果使用System.Threading
会有问题,是否有理由对BackgroundWorker
这样的问题进行处理?