任务在等待很长时间内处于WaitingToRun状态

时间:2013-02-01 06:33:09

标签: c# concurrency task

我有一个程序可以处理并行运行的各种任务。单个任务充当各种管理器,确保在下一个任务运行之前满足某些条件。但是,我发现有时任务将处于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延迟我的任务安排的原因?我是否在使一些潜在资源饱和?这是怎么回事?

3 个答案:

答案 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());

希望这对某人有帮助,因为它使我困扰了好一阵子!