拥有无限工作线程的正确方法?

发布于 2024-08-07 16:40:06 字数 640 浏览 10 评论 0原文

我有一个需要大量初始化的对象(在强大的机器上需要 1-2 秒)。尽管一旦初始化,它只需要大约 20 毫秒来完成一项典型的“工作”,

以防止每次应用程序想要使用它时都重新初始化它(可能是每秒 50 次,或者几分钟内根本不初始化)在典型用法中),我决定给它一个作业队列,并让它在自己的线程上运行,检查队列中是否有任何工作。然而,我不完全确定如何创建一个在有或没有工作的情况下无限期运行的线程。

这是我到目前为止所得到的,欢迎任何批评

    private void DoWork()
    {
        while (true)
        {
            if (JobQue.Count > 0)
            {
                // do work on JobQue.Dequeue()
            }
            else
            {
                System.Threading.Thread.Sleep(50);
            }
        }
    }

经过深思熟虑:我想我可能需要优雅地终止这个线程,而不是让它永远运行,所以我想我将添加一个 Job 类型来告诉线程结束。任何有关如何结束这样的线程的想法也表示赞赏。

I have an object that requires a lot of initialization (1-2 seconds on a beefy machine). Though once it is initialized it only takes about 20 miliseconds to do a typical "job"

In order to prevent it from being re-initialized every time an app wants to use it (which could be 50 times a second or not at all for minutes in typical usage), I decided to give it a job que, and have it run on its own thread, checking to see if there is any work for it in the que. However I'm not entirely sure how to make a thread that runs indefinetly with or without work.

Here's what I have so far, any critique is welcomed

    private void DoWork()
    {
        while (true)
        {
            if (JobQue.Count > 0)
            {
                // do work on JobQue.Dequeue()
            }
            else
            {
                System.Threading.Thread.Sleep(50);
            }
        }
    }

After thought: I was thinking I may need to kill this thread gracefully insead of letting it run forever, so I think I will add a Job type that tells the thread to end. Any thoughts on how to end a thread like this also appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

陈年往事 2024-08-14 16:40:06

无论如何,您需要锁定,这样您就可以等待脉冲

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); }
}

您可能还想看看这个问题,它限制了队列的大小(如果太满了)。

You need to lock anyway, so you can Wait and 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
}

with add like:

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); }
}

You might also want to look at this question, which limits the size of the queue (blocking if it is too full).

当梦初醒 2024-08-14 16:40:06

您需要一个同步原语,例如 WaitHandle (查看静态方法)。通过这种方式,您可以向工作线程发出“信号”,表明有工作。它检查队列并继续工作,直到队列为空,此时它等待互斥体再次发出信号。

使其中一个作业项也成为退出命令,以便您可以在需要退出线程时向工作线程发出信号

You need a synchronization primitive, like a WaitHandle (look at the static methods) . This way you can 'signal' the worker thread that there is work. It checks the queue and keeps on working until the queue is empty, at which time it waits for the mutex to signal it again.

Make one of the job items be a quit command too, so that you can signal the worker thread when it's time to exit the thread

江湖彼岸 2024-08-14 16:40:06

在大多数情况下,我的做法与您的设置方式非常相似,但使用的语言不同。我的优势是使用数据结构(在Python中),它会阻塞线程,直到有一个项目被放入队列,从而不需要睡眠调用。

如果 .NET 提供了这样的类,我会考虑使用它。线程阻塞比线程在睡眠调用上旋转要好得多。

你可以通过的工作可以像“null”一样简单;如果代码接收到一个空值,它就知道是时候退出 while 并回家了。

In most cases, I've done this quite similar to how you've set up -- but not in the same language. I had the advantage of working with a data structure (in Python) which will block the thread until an item is put into the queue, negating the need for the sleep call.

If .NET provides a class like that, I'd look into using it. A thread blocking is much better than a thread spinning on sleep calls.

The job you can pass could be as simple as a "null"; if the code receives a null, it knows it's time to break out of the while and go home.

樱娆 2024-08-14 16:40:06

如果您确实不需要线程退出(只是希望它阻止您的应用程序运行),您可以设置 Thread.IsBackground 为 true,当所有非后台线程结束时它将结束。 Will 和 Marc 对于处理队列都有很好的解决方案。

If you don't really need to have the thread exit (and just want it to keep from keeping your application running) you can set Thread.IsBackground to true and it will end when all non background threads end. Will and Marc both have good solutions for handling the queue.

梦旅人picnic 2024-08-14 16:40:06

获取并行框架。它有一个 BlockingCollection,您可以使用它可以用作作业队列。您将如何使用它:

  1. 创建 BlockingCollection;这将保留您的任务/工作。
  2. 创建一些具有永无止境的循环的线程 (while(true){ // get job off the queue)
  3. 设置线程进行
  4. 当作业可用时将其添加到集合中

线程将被阻塞,直到集合中出现项目为止。无论轮到谁都会得到它(取决于CPU)。我现在正在使用这个并且效果很好。

它还具有依赖 MS 编写多线程访问同一资源的特别令人讨厌的代码的优点。只要你能让别人写信,你就应该这么做。当然,假设他们比您拥有更多的技术/测试资源和综合经验。

Grab the Parallel Framework. It has a BlockingCollection<T> which you can use as a job queue. How you'd use it is:

  1. Create the BlockingCollection<T> that will hold your tasks/jobs.
  2. Create some Threads which have a never-ending loop (while(true){ // get job off the queue)
  3. Set the threads going
  4. Add jobs to the collection when they come available

The threads will be blocked until an item appears in the collection. Whoever's turn it is will get it (depends on the CPU). I'm using this now and it works great.

It also has the advantage of relying on MS to write that particularly nasty bit of code where multiple threads access the same resource. And whenever you can get somebody else to write that you should go for it. Assuming, of course, they have more technical/testing resources and combined experience than you.

浅沫记忆 2024-08-14 16:40:06

我实现了一个后台任务队列,没有使用任何类型的 while 循环、脉冲、等待,或者实际上根本没有接触 Thread 对象。这似乎有效。 (我的意思是,在过去 18 个月里,它在生产环境中每天处理数千个任务,没有任何意外行为。)它是一个具有两个重要属性的类,一个 Queue 和一个 <代码>后台工作人员。共有三种重要的方法,这里简述一下:

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 内部。每当队列中删除任务时,它就会被唤醒,运行直到队列为空,然后返回睡眠状态。

我远不是线程方面的专家。如果使用 BackgroundWorker 就可以解决这样的问题,是否有理由使用 System.Threading 来解决这样的问题?

I've implemented a background-task queue without using any kind of while loop, or pulsing, or waiting, or, indeed, touching Thread objects at all. And it seems to work. (By which I mean it's been in production environments handling thousands of tasks a day for the last 18 months without any unexpected behavior.) It's a class with two significant properties, a Queue<Task> and a BackgroundWorker. There are three significant methods, abbreviated here:

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();
   }
}

It's not that there's no waiting and pulsing. But that all happens inside the BackgroundWorker. This just wakes up whenever a task is dropped in the queue, runs until the queue is empty, and then goes back to sleep.

I am far from an expert on threading. Is there a reason to mess around with System.Threading for a problem like this if using a BackgroundWorker will do?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文