我有一个与设备一起工作的类,为了避免在使用设备和串口时我使用线程的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());
}
}
答案 0 :(得分:2)
这些任务内置了一些内容:ActionBlock
(不与核心框架一起分发,您需要添加Microsoft.Tpl.Dataflow
NuGet包。)
如果您创建它:
var actionBlock = new ActionBlock<TASK>(t => DoWork(t),
new ExecutionDataflowBlockOptions() { MaxDegreeOfParallelism = 1 });
一次只能处理一个项目。
然后你只需致电:
actionBlock.Post(yourTask);
并且所有“已发布”的任务在他们自己的线程中一个接一个地执行。
完成后,您可以拨打actionBlock.Complete();
,然后再拨打{0} await actionBlock.Completion;
。在那里,您还可以检查DoWork
内发生的异常。
答案 1 :(得分:2)
有没有办法重用线程?因为我只需要一个线程 做任务是不是一个好主意使用线程池?
是的,使用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实现可以节省您在启动任务处理和旋转后台线程时的一些开销,同时没有任何要处理的项目。我发布了这个答案给你另一种解决问题的方法。