我有一个程序可以处理并行运行的各种任务。单个任务充当各种管理器,确保在下一个任务运行之前满足某些条件。但是,我发现有时任务将处于WaitingToRun状态很长一段时间。这是以下代码:
mIsDisposed = false;
mTasks = new BlockingCollection<TaskWrapper>(new ConcurrentQueue<TaskWrapper>());
Task.Factory.StartNew(() => {
while (!mIsDisposed) {
var tTask = mTasks.Take();
tTask.task.Start();
while (tTask.task.Status == TaskStatus.WaitingToRun) {
Console.WriteLine("Waiting to run... {0}", tTask.task.Id);
Thread.Sleep(200);
}
tTask.ready.Wait();
}
mTasks.Dispose();
});
DoWork();
DoWork();
DoWork();
DoWork();
DoWorkAsync();
DoWorkAsync();
DoWorkAsync();
DoWorkAsync();
DoWorkAsync();
DoWork();
TaskWrapper非常简单地定义为:
private class TaskWrapper
{
public Task task { get; set; }
public Task ready { get; set; }
}
目前只在两个地方添加了任务:
public void DoWork()
{
DoWorkAsync().Wait();
}
public Task DoWorkAsync()
{
ManualResetEvent next = new ManualResetEvent(false);
Task task = new Task(() => ActualWork(next));
Task ready = Task.Factory.StartNew(() => next.Wait());
mTasks.Add(new TaskWrapper() {
task = task,
ready = ready
});
return task;
}
ActualWork(next)
调用next.Set()
。
此队列工作并等待next
设置,然后才允许继续下一个工作项。您可以等待整个任务完成,然后再继续调用DoWork()
或一次排队多个任务(应该在next
设置后运行)。
然而,当通过DoWorkAsync()
添加任务时,在调用tTask.task.Start()
后,tTask.task
处于WaitingToRun状态一段时间(如30秒到一分钟),然后神奇地启动运行。我使用while循环监视了这个,Waiting To Run... #
将显示相当长的时间。
呼叫DoWork()
始终立即运行。我确信这与在设置为运行的任务上调用Wait
有关。
我在这里不知所措。
更新
我设法使代码工作,但我仍然想知道为什么首先出现问题。
经过一些实验性的改变,我已经设法解决了我自己的问题,但它更像是“哦,所以我不能这样做”而不是一个好的修复。事实证明我的问题是将任务排队以太快运行。通过将DoWorkAsync()
修改为不再使用Task.Factory.StartNew
并将tTask.ready.Wait()
更改为tTask.ready.RunSynchronously
,我已成功解决了我的问题。
是否有TaskScheduler
延迟我的任务安排的原因?我是否在使一些潜在资源饱和?这是怎么回事?
答案 0 :(得分:4)
线程将在系统的线程池中运行。线程池始终具有最小可用线程数(请参阅ThreadPool.SetMinThreads())。如果您尝试创建多个线程,则每个新线程启动之间将引入大约500毫秒的延迟。
线程池中还有最大线程数(请参阅ThreadPool.GetMaxThreads()),如果达到该限制,则不会创建新线程;它将等到旧线程在调度新线程之前死亡(或者更确切地说,重新安排旧线程以运行新线程)。
你不太可能达到这个限制 - 它可能超过1000。
答案 1 :(得分:0)
正面临类似的问题。
我有一堆运行inifite循环的类似任务,其中一项任务时不时地保持在WaitingToRun状态。
以这种方式创建任务对我有用:
_task = new Task(() => DoSmth(_cancellationTokenSource.Token), TaskCreationOptions.LongRunning);
_task.Start();
答案 2 :(得分:0)
好的,我刚刚遇到过类似的问题。运行了一些创建和启动任务的代码,但是任务从未启动(它只是将状态更改为WaitingToRun)
尝试了该线程中的其他选项后无济于事,我再想了一下,然后意识到调用此方法的代码本身是在延续任务中调用的,该任务已指定要在UI任务上运行调度程序(需要更新UI)...
类似
void Main()
{
var t1 = new Task(() => Console.WriteLine("hello, I'm task t1"));
t1.ContinueWith(t => CreateAndRunASubTask(), TaskScheduler.FromCurrentSynchronizationContext());
t1.Start();
Console.WriteLine("All tasks done with");
}
// Define other methods and classes here
public void CreateAndRunASubTask()
{
var tsk = new Task(() => Console.WriteLine("hello, I'm the sub-task"));
tsk.Start();
Console.WriteLine("sub-task has been told to start");
tsk.Wait();
// the code blocks on tsk.Wait() indefinately, the tsk status being "WaitingToRun"
Console.WriteLine("sub-task has finished");
}
该修复程序非常简单-在指定继续任务时,您需要指定TaskContinuationOption:TaskContinuationOptions.HideScheduler
这具有...(摘自XML注释)的作用
指定通过调用方法继续创建的任务 例如System.Threading.Tasks.Task.Run(System.Action)或 System.Threading.Tasks.Task.ContinueWith(System.Action {System.Threading.Tasks.Task}) 请参阅默认调度程序(System.Threading.Tasks.TaskScheduler.Default)而不是
而不是作为当前调度程序运行此延续的调度程序。
即(在我的示例中)
t1.ContinueWith(t =>
CreateAndRunASubTask(),
System.Threading.CancellationToken.None,
TaskContinuationOptions.HideScheduler,
TaskScheduler.FromCurrentSynchronizationContext());
希望这对某人有帮助,因为它使我困扰了好一阵子!