9

这是一个控制台程序,希望 10 个线程批量启动,等待 5 秒,然后批量停止。

static void Main(string[] args)
{
    System.Threading.Tasks.Parallel.For(0, 10, (index) =>
    {
        Action<int> act = (i) =>
        {
            Console.Write("start {0} ", i);
            Thread.Sleep(5000);
        };

        act.BeginInvoke(index, OnTaskComplete, index);
    });

    Console.ReadKey();
}

static void OnTaskComplete(IAsyncResult res)
{
    Console.Write("finish {0} ", res.AsyncState);
}

但结果不是我所期望的,10 个线程一个接一个地启动(大约 1 秒间隔),甚至一些“完成”在一些“开始”之前出现。

当注释掉 Thread.Sleep 时,所有线程都在 flash 中开始和结束。

Thread.Sleep会影响其他线程吗?反正有没有纯粹的空闲时间?

/ - - - - - - - - - - - - - - -编辑 - - - - - - - - - - ----------

同样的问题也发生在:

static void Main(string[] args)
{
    System.Threading.Tasks.Parallel.For(0, 10, (index) =>
    {
        Console.Write("start {0} ", index);
        Thread.Sleep(5000);
        Console.Write("fnish {0} ", index);
    });

    Console.ReadKey();
}

- - - - - - - - - - - 编辑 - - - - - - - - - - - -

最后我找到了一个可爱的方法来代替 thread.sleep

static void Main(string[] args)
{
    System.Threading.Tasks.Parallel.For(0, 10, (index) =>
    {
        Console.Write("start {0} ", index);

        var t1 = new System.Threading.Timer(new TimerCallback(MyTimerCallback), index, 5000, 0); 
    });

    Console.ReadKey();
}

static void MyTimerCallback(object o)
{
    Console.Write("Timer callbacked ");
}
4

3 回答 3

13

这是设计使然。您看到线程池管理器试图将有限数量的线程保持在执行状态。重要的是要确保您的程序运行的线程数不超过您的机器的 cpu 内核数。这是低效的,当 Windows 被迫开始在活动线程之间交换内核时,完成的工作更少。线程池管理器不够聪明,无法知道线程正在休眠并且实际上没有执行任何工作。

在双核机器上,您会看到前 2 个线程立即启动。然后,当线程管理器注意到活动线程没有取得任何进展并且可能被阻塞时,允许额外的线程以一秒的间隔一个接一个地运行。线程被释放和执行 Console.Write() 调用的顺序是不确定的。

这当然是人为的测试,真正的线程不会休眠。如果您有长时间阻塞的线程,例如等待 I/O 请求完成,那么使用线程池线程(任务)不是最佳解决方案。

于 2011-06-01T03:04:28.357 回答
5

TaskCreationOptions.LongRunning将“删除” ThreadPool 限制。
我不知道指定TaskCreationOptions.LongRunningfor的简单方法Parallel.For
但是,您可以使用 Task 类实现相同的效果:

Action<int> action = i =>
    {
        Console.Write("start {0} ", i);
        Thread.Sleep(5000);
        Console.Write("finish {0} ", i);
    };

var tasks = Enumerable.Range(0, 100)
    .Select(arg => Task.Factory.StartNew(() => action(arg), TaskCreationOptions.LongRunning))
    .ToArray();

Task.WaitAll(tasks);

没有TaskCreationOptions.LongRunning它,它将以与您完全相同的方式运行Parallel.For

于 2011-06-01T03:16:38.493 回答
3

我稍微更新了代码以在写入控制台时显示 ThreadID:

Console.WriteLine("start index:{0} thread id:{1} Time:{2} ", index, Thread.CurrentThread.ManagedThreadId.ToString(), DateTime.Now.ToLongTimeString());
Thread.Sleep(5000);
ConsoleWriteLine("finish index:{0} thread id:{1} Time:{2} ", index, Thread.CurrentThread.ManagedThreadId.ToString(), DateTime.Now.ToLongTimeString());

我的机器是双核的,这是我得到的输出。这应该让您了解正在发生的事情。请记住,循环可能并不总是按顺序运行,即。从 0 到 9,它是并行的,它会抓取你数组的一块并通过 lambda 运行每个项目。

输出:

start  index:1 thread id:11 Time:11:07:17 PM
start  index:0 thread id:9  Time:11:07:17 PM
start  index:5 thread id:10 Time:11:07:17 PM
start  index:6 thread id:12 Time:11:07:18 PM
start  index:2 thread id:13 Time:11:07:19 PM
start  index:7 thread id:14 Time:11:07:20 PM
start  index:3 thread id:15 Time:11:07:21 PM
start  index:8 thread id:16 Time:11:07:22 PM
finish index:0 thread id:9  Time:11:07:22 PM
start  index:4 thread id:9  Time:11:07:22 PM
finish index:1 thread id:11 Time:11:07:22 PM
start  index:9 thread id:11 Time:11:07:22 PM
finish index:5 thread id:10 Time:11:07:22 PM
finish index:6 thread id:12 Time:11:07:23 PM
finish index:2 thread id:13 Time:11:07:24 PM
finish index:7 thread id:14 Time:11:07:25 PM
finish index:3 thread id:15 Time:11:07:26 PM
finish index:8 thread id:16 Time:11:07:27 PM
finish index:4 thread id:9  Time:11:07:27 PM
finish index:9 thread id:11 Time:11:07:27 PM
于 2011-06-01T03:12:01.273 回答