【发布时间】:2011-07-01 16:19:44
【问题描述】:
在下面的代码中,我想同步报告任务列表的结果。这现在有效,因为 task.Result 阻塞,直到任务完成。但是,任务 id = 3 需要很长时间才能完成,并阻止所有其他已完成的任务报告其状态。
我认为我可以通过将报告 (Console.Write) 移动到 .ContinueWith 指令中来做到这一点,但我没有 UI 线程,那么如何让 TaskScheduler 同步 .ContinueWith 任务?
我现在拥有的:
static void Main(string[] args)
{
Console.WriteLine("Starting on {0}", Thread.CurrentThread.ManagedThreadId);
var tasks = new List<Task<int>>();
for (var i = 0; i < 10; i++)
{
var num = i;
var t = Task<int>.Factory.StartNew(() =>
{
if (num == 3)
{
Thread.Sleep(20000);
}
Thread.Sleep(new Random(num).Next(1000, 5000));
Console.WriteLine("Done {0} on {1}", num, Thread.CurrentThread.ManagedThreadId);
return num;
});
tasks.Add(t);
}
foreach (var task in tasks)
{
Console.WriteLine("Completed {0} on {1}", task.Result, Thread.CurrentThread.ManagedThreadId);
}
Console.WriteLine("End of Main");
Console.ReadKey();
}
我想转移到这个或类似的东西,但我需要 Console.Write("Completed...") 都发生在同一个线程上:
static void Main(string[] args)
{
Console.WriteLine("Starting on {0}", Thread.CurrentThread.ManagedThreadId);
for (var i = 0; i < 10; i++)
{
var num = i;
Task<int>.Factory.StartNew(() =>
{
if (num == 3)
{
Thread.Sleep(20000);
}
Thread.Sleep(new Random(num).Next(1000, 10000));
Console.WriteLine("Done {0} on {1}", num, Thread.CurrentThread.ManagedThreadId);
return num;
}).ContinueWith(value =>
{
Console.WriteLine("Completed {0} on {1}", value.Result, Thread.CurrentThread.ManagedThreadId);
}
/* need syncronization context */);
}
Console.WriteLine("End of Main");
Console.ReadKey();
}
-- 解决方案-- 在获得了一些 cmets 并阅读了一些解决方案之后,这就是我想要的完整解决方案。这里的目标是尽可能快地处理多个长时间运行的任务,然后一次处理每个任务的结果。
static void Main(string[] args)
{
Console.WriteLine("Starting on {0}", Thread.CurrentThread.ManagedThreadId);
var results = new BlockingCollection<int>();
Task.Factory.StartNew(() =>
{
while (!results.IsCompleted)
{
try
{
var x = results.Take();
Console.WriteLine("Completed {0} on {1}", x, Thread.CurrentThread.ManagedThreadId);
}
catch (InvalidOperationException)
{
}
}
Console.WriteLine("\r\nNo more items to take.");
});
var tasks = new List<Task>();
for (var i = 0; i < 10; i++)
{
var num = i;
var t = Task.Factory.StartNew(() =>
{
if (num == 3)
{
Thread.Sleep(20000);
}
Thread.Sleep(new Random(num).Next(1000, 10000));
Console.WriteLine("Done {0} on {1}", num, Thread.CurrentThread.ManagedThreadId);
results.Add(num);
});
tasks.Add(t);
}
Task.Factory.ContinueWhenAll(tasks.ToArray(), _ => results.CompleteAdding());
Console.WriteLine("End of Main");
Console.ReadKey();
}
【问题讨论】:
-
我假设控制台线程是 GUI (WinForms/WPF) 的替身。这不是一个好主意,Dispatcher/Messageloop 的存在会产生(很大)差异。
-
否则,想想你所说的“发生在同一个线程上”是什么意思。除非该线程正在轮询,否则不能这样做。
-
我需要更改它,以便一次只运行一个 ContinueWith 任务。它们是否在同一个线程上运行并不重要,但我不能让它们中的两个并行运行。在现实生活中,我的 ContinueWith 部分是将大量数据写入数据库。
-
所以你实际上有一个 n-Producer/1-Consumer 模式。
-
这与在特定线程上写入控制台无关......我修改后的答案是合适的,可以扩展到“将大量数据写入数据库”。
标签: c# task task-parallel-library