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

问题描述:

我有一个与设备一起工作的类,以避免在使用设备和串行端口时UI层出现延迟我使用了一个线程,但我的设备仅限于做一次一份工作。 所以,我有一个队列,当用户要求做任务被添加到它时,然后我运行一个线程来逐个完成任务。使用一个线程多次执行特定任务C#

每次用户要求一个任务时,我会检查线程是否正在运行,如果是,我只是将新任务添加到队列中,如果没有,我再次创建线程。这意味着每次队列都变空时,我应该创建一个新线程。现在我想问问有没有什么方法可以重用该线程?由于我只需要一个线程来完成任务,使用线程池是个好主意?由于挂起是一种过时的方法,我不知道用户什么时候会要求另一个任务使用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()); 
    } 
} 
+3

为什么写自己的ThreadPool?该框架已经提供了一个。 – 2014-09-28 08:50:38

+0

如何使用线程池来代替?听起来像是你想要的。 – Automatico 2014-09-28 08:52:06

+0

@HenkHolterman在多线程的情况下不是线程池有用吗?我的意思是我不能运行更多的一个线程从队列中选择任务并将它们传递到调制解调器,实际上我需要一个线程来逐个完成任务!或者我误解了threadpool? – 2014-09-28 08:58:15

有没有什么办法重用线程?由于我只需要一个线程到 做任务是使用线程池的好主意吗?

是的,使用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来取消当前正在处理的项目。请注意,您将不得不添加适当的异常处理,取消等。

@ChrFin使用TPL Dataflow实现可以节省您在开始处理任务和旋转后台线程时的一些开销,同时没有任何项目需要处理。我发布了这个答案给你另一种解决你的问题的方式。

+0

为什么实施自己的“生产者 - 消费者”逻辑如果已经有内置的?你将如何处理“*运行任务”中的错误? – ChrFin 2014-09-28 09:04:51

+0

这是如何实现我自己的生产者 - 消费者逻辑?这只是使用内置类型来满足他需要的内容。我不确定OP是否需要“TPL Dataflow”的全面架构,尤其是当您将其并发限制为单个线程时?我将如何处理错误?我不确定你是什么意思。这与在任何队列处理中处理错误有什么不同? – 2014-09-28 09:06:35

+0

正如我在回答中所说,这是一个相当简单的例子。 OP将需要采取这种做法,使其适用于所有可能的场景,包括添加错误处理机制。我不是说这个例子比使用ActionBlock更好,它只是一个不同的例子。它由OP来决定他想要使用什么。 – 2014-09-28 09:12:18

也有一些是内置了这样的任务:ActionBlock(不与核心框架分布,你需要添加Microsoft.Tpl.Dataflow NuGet包)。
如果你创建喜欢:

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

只有一个项目将立即进行处理。

然后你只需拨打:

actionBlock.Post(yourTask); 

和所有“贴”任务是在自己的线程中执行一个后对方。

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