【问题标题】:How to force Task.Factory.StartNew to a background thread?如何强制 Task.Factory.StartNew 到后台线程?
【发布时间】:2013-04-04 03:25:44
【问题描述】:

我已经看到许多其他类似的问题,但没有在那里找到我的答案。

我的问题是我正在使用以下流程创建线程:

private void btn_Click(object sender, EventArgs e)
{
    service.GetCount(
        (count, ex) =>
        {
            if (ex != null)
                return;

            for (int i = 0; i < count; i++)
            {
                service.Get(onItemReceived, i);
            }
        }
    );
}

public void GetCount(Action<int, Exception> callback)
{
    var callingThread = TaskScheduler.FromCurrentSynchronizationContext();

    Func<int> action = () =>
    {
        return client.GetCount(); // Synchronous method, could take a long time
    };

    Action<Task<int>> completeAction = (task) =>
    {
        Exception ex = (task.Exception != null) ? task.Exception.InnerException : task.Exception;

        if (callback != null)
            callback(task.Result, ex);
    };

    Task.Factory.StartNew(action).ContinueWith(completeAction, callingThread);
}

public void Get(Action<object, Exception> callback, int index)
{
    var callingThread = TaskScheduler.FromCurrentSynchronizationContext();

    Func<object> action = () =>
    {
        return client.Get(index); // Synchronous method, could take a long time
    };

    Action<Task<object>> completeAction = (task) =>
    {
        Exception ex = (task.Exception != null) ? task.Exception.InnerException : task.Exception;

        if (callback != null)
            callback(task.Result, ex);
    };

    Task.Factory.StartNew(action).ContinueWith(completeAction, callingThread);
}

这样,我的每个服务的异步方法都会回调它们最初调用的线程(通常是 UI 线程)。所以我在模拟 await/async 关键字的工作方式(我不能使用 .NET 4.5)。

这种模式的问题是,在第一次调用“ContinueWith”后,我莫名其妙地锁定到 UI 线程。因此,在这种情况下,如果我尝试为每个进程生成 5 个线程同步函数 Get,它们将逐个执行而不是并行执行,并且在执行此操作时它们会阻塞 UI 线程,即使我尝试指定 TaskCreationOptions.LongRunning。

这不会发生在我第一次调用 Task.Factory.StartNew 时,它只会发生在从第一个回调中的后续调用中。

【问题讨论】:

标签: c# .net-4.0 task threadpool


【解决方案1】:

为了强制启动新线程,您应该在对 Task.Factory.StartNew 的调用中指定 TaskScheduler.Default,如下所示:

Task.Factory.StartNew(action,
                      CancellationToken.None,
                      TaskCreationOptions.None,
                      TaskScheduler.Default).ContinueWith(completeAction);

在我的测试中,您不需要指定 TaskCreationOptions.LongRunning 来强制使用后台线程,尽管它不应该受到伤害。

【讨论】:

  • LongRunning 强制创建一个全新的非线程池线程
  • @Servy - 它不会强制执行任何操作,它是调度程序的提示
  • @HenkHolterman 从理论上讲,是的,这是正确的。实际上,在当前实现中,它会导致创建一个新线程。虽然我同意最好假设它可能不会运行,但您应该假设将任务标记为长时间运行而实际上不是会消耗额外的资源。
  • 在我上面问题中列出的场景中,TaskCreationOptions.LongRunning 不会强制使用新的后台线程。只使用了 TaskScheduler.Default 。
  • @Servy 提出这个问题的原因是,如果有人依赖这个实现细节(以下任务将在与当前线程不同的线程上执行),它如果违反假设,可能会导致死锁(恰当地称为“自死锁”或“自等待死锁”)。
猜你喜欢
  • 1970-01-01
  • 2017-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多