使用一个线程多次执行特定任务C#

本文关键字:执行 任务 线程 一个 | 更新日期: 2023-09-27 18:24:54

我有一个可以与设备一起工作的类,在与设备和串行端口一起工作时,为了避免UI层的延迟,我使用了一个线程,但我的设备一次只能做一项工作。所以,我有一个队列,当用户要求做某事时,任务被添加到队列中,然后我运行一个线程来逐个执行任务。

每次用户请求任务时,我都会检查线程是否正在运行,如果是,我只会将新任务添加到队列中,如果不是,我会再次创建线程。这意味着每次队列变空时,我都应该创建一个新线程。现在我想问一下,有什么方法可以重用线程吗?既然我只需要一个线程来完成任务,那么使用线程池是个好主意吗?由于suspend是一个过时的方法,我不知道用户什么时候会要求另一个任务使用wait(),我可以挂起线程并以其他方式再次运行它吗?还是最好再次创建线程,而我做得对?

public class Modem
{
    Thread thread;
    Queue<Task> Tasks = new Queue<Task>();
    public Modem()
    {
    }
    public void DoTask(Task s)
    {
        Tasks.Enqueue(s);
        if (thread == null || !thread.IsAlive)
           thread = new Thread(HandleTasks);
    }
    private void HandleTasks()
    {
        while (Tasks.Count != 0)
            SendTaskToModem(Tasks.Dequeue());
    }
}

使用一个线程多次执行特定任务C#

有什么方法可以重用线程吗?因为我只需要一个线程做任务使用线程池是个好主意吗?

是的,使用ThreadPool并依赖于框架通常比构建自己的自定义实现更好。

您可以使用一个后台线程,该线程负责处理您的项目,而另一个线程负责对它们进行排队。这是一个简单的生产者-消费者问题。为此,您可以使用BlockingCollection:

public class Modem
{
    private BlockingCollection<Task> _tasks;
    public Modem()
    {
       _tasks = new BlockingCollection<Task>();
    }
    public void QueueTask(Task s)
    {
        _tasks.Add(s);
    }
    private Task StartHandleTasks()
    {
         return Task.Run(async () =>
         {
             while (!_tasks.IsCompleted)
             {
                 if (_tasks.Count == 0)
                 {
                     await Task.Delay(100).ConfigureAwait(false);
                     continue;
                 }
                 Task task;
                 _tasks.TryTake(out task);
                 if (task != null)
                 {
                    // Process item.
                 }
             }
         });
    }
}

这是使用BlockingCollection的一个相当简单的例子。它有更多的内置功能,例如使用CancellationToken取消当前正在处理的项目。请注意,您必须添加适当的异常处理、取消等。

@使用TPL Dataflow实现ChrFin可以在没有任何项目要处理的情况下启动任务处理和旋转后台线程,从而节省一些开销。我发布这个答案是为了给你另一种解决问题的方法。

这类任务内置了ActionBlock(不与核心框架一起分发,需要添加Microsoft.Tpl.DataflowNuGet包)
如果你创建它像:

var actionBlock = new ActionBlock<TASK>(t => DoWork(t), 
    new ExecutionDataflowBlockOptions() { MaxDegreeOfParallelism = 1 });

一次只处理一个项目。

然后你只需拨打:

actionBlock.Post(yourTask);

并且所有"发布"的任务都在它们自己的线程中一个接一个地执行。

完成后,您可以拨打actionBlock.Complete();,然后拨打例如await actionBlock.Completion;。在那里,您还可以检查DoWork内部发生的异常。