【问题标题】:What is keeping the TPL from spinning up another thread是什么让 TPL 无法启动另一个线程
【发布时间】:2013-01-17 04:05:49
【问题描述】:

使用命令模式返回仅休眠 5 秒的任务,完成 3 个任务的总时间约为 15 秒。

我在做什么来阻止这段代码“并行”执行?

调用代码

        var timer = new Stopwatch();

        timer.Start();

        var task = CommandExecutor.ExecuteCommand(new Fake());
        var task2 = CommandExecutor.ExecuteCommand(new Fake());
        var task3 = CommandExecutor.ExecuteCommand(new Fake());

        Task.WaitAll(new Task[]
        {
            task, task2, task3
        });

        timer.Stop();

        Debug.Print("{0}ms for {1},{2},{3} records", timer.ElapsedMilliseconds, task.Result.Count, task2.Result.Count(), task3.Result.Count());

正在执行的命令

public class Fake : Command<Task<Dictionary<string, string[]>>>
{
    public override string ToString()
    {
        return string.Format("Fake");
    }

    protected override void Execute()
    {
        new System.Threading.ManualResetEvent(false).WaitOne(5000);

        Result = Task.Factory.StartNew(() => new Dictionary<string, string[]>());
    }
}

命令抽象

public abstract class Command
{
    public void Run()
    {
        try
        {
            var timer = new Stopwatch();
            timer.Start();
            //Debug.Print("{0}-{1}", ToString(), "Executing");
            Execute();
            timer.Stop();
            Debug.Print("{0}-{1} Duration: {2}ms", ToString(), "Done", timer.ElapsedMilliseconds.ToString(CultureInfo.InvariantCulture));             
        }
        catch (Exception ex)
        {
            Debug.Print("Error processing task:" + ToString(), ex);
        }
    }

    public abstract override string ToString();

    protected abstract void Execute();
}

/// <summary>
/// A command with a return value
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class Command<T> : Command
{
    public T Result { get; protected set; }

    public T GetResult()
    {
        Run();
        return Result;
    }
}

命令执行器

public class CommandExecutor
{
    /// <summary>
    ///     Executes the command.
    /// </summary>
    /// <param name="cmd">The CMD.</param>
    public static void ExecuteCommand(Command cmd)
    {
        cmd.Run();
    }

    /// <summary>
    ///     Executes the command for commands with a result.
    /// </summary>
    /// <typeparam name="TResult">The type of the result.</typeparam>
    /// <param name="cmd">The CMD.</param>
    /// <returns></returns>
    public static TResult ExecuteCommand<TResult>(Command<TResult> cmd)
    {
        ExecuteCommand((Command) cmd);

        return cmd.Result;
    }

【问题讨论】:

  • 您应该在此处发布相关代码。如果您在这里发布太多内容,那么您应该简化示例。
  • 我相信幕后有一个工作项调度程序,它具有启发式方法来确定何时并行运行任务、创建新线程、结束现有线程等。如果您创建 1,000,000 个任务它不会立即创建那么多线程,它会弄清楚如何最好地执行它们,因为那么多线程会杀死 CPU。一个需要 5 秒才能完成的任务可能不是这个启发式的最佳选择。请参阅stackoverflow.com/questions/3105988/… 了解更多信息。
  • 1) 您甚至还没有提供所有代码。您尚未提供CommandExecutorMoreFakes 的定义。我们应该能够复制粘贴这样的示例并运行它。 2) 你这里有太多不需要的代码。其中绝大多数实际上并不重要。使用Task 对象的要点是您不需要这些类型的抽象。
  • @ta.speot.is 这不是这里的问题。他的编码方式使得它们根本不可能并行化。
  • @Servy 为你点赞。

标签: c# task-parallel-library


【解决方案1】:

问题在于您不是在实际的 Task 对象中等待,而是在创建任务的方法中等待它实际提供该任务:

protected override void Execute()
{
    new System.Threading.ManualResetEvent(false).WaitOne(5000);

    Result = Task.Factory.StartNew(() => new Dictionary<string, string[]>());
}

应该是:

protected override void Execute()
{
    Result = Task.Factory.StartNew(() =>
    {
        new System.Threading.ManualResetEvent(false).WaitOne(5000);
        return new Dictionary<string, string[]>();
    });
}

【讨论】:

  • @Steve 这就是为什么我马上告诉你你使用了太多不需要的代码。您应该只有一个返回任务的静态方法(基本上是此方法,但返回任务),然后在三个实例上使用 WaitAll。这就是演示这个问题所需要的全部内容。
  • 是的,当问题如此简单时,很容易责怪其他代码。
  • @Steve 这就是为什么您应该在发布问题之前尽可能简化代码的原因之一:您可能会在此期间发现错误的实际位置。
猜你喜欢
  • 2020-09-07
  • 2013-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多