等待后台线程完成

5

很抱歉如果这是一个重复的问题,但我不太确定需要使用哪些术语才能找到现有的答案。

我正在尝试改进应用程序的启动性能,伪代码看起来有点像这样。

LoadBigFileFromDisk();  //slow
SetupNetwork();         //even slower
UseBigFileFromDisk();

我发现第一步是磁盘绑定的,而另一个步骤是网络绑定(并且较慢),我可以在后台线程中运行第一步(目前正在尝试使用ThreadPool.QueueUserWorkItem,但不确定是否最佳选择),从而提高性能。

它能够工作,但让我担心的是我依赖第二步足够慢以便第一步完成。
我知道我可以在某个地方设置一个_done boolean,并在while上面加!,但是否有更优雅/惯用的解决方案?
(还没有 .Net 4.0,因此虽然我对基于任务的内容很感兴趣,但我需要其他解决方案)。
3个回答

3
你可以尝试以下方法:
var loadTask=Task.Factory.StartNew(()=>LoadBigFileFromDisk());
var setupTask=Task.Factory.StartNew(()=>SetupNetwork());

Task.WaitAll(loadTask,setupTask);

UseBigFileFromDisk();

这使用了任务并行库。
或者:
var loadThread=new Thread(()=>LoadBigFileFromDisk());
var setupThread=new Thread(()=>SetupNetwork());

loadThread.Start();
setupThread.Start();

loadThread.Join();
setupThread.Join();

UseBigFileFromDisk();

当您没有使用.NET 4时。如果这些任务运行时间很长,则最好避免使用线程池,因为它主要用于短期任务。


3
在“主类”中执行以下操作:
ManualResetEvent mre = new ManualResetEvent(false);

在你的“main”方法中,按照以下方式操作:
// Launch the tasks

mre.WaitOne();

在任务完成时(就在返回之前:-)),请注意。
mre.Set();

如果您需要等待多个事件,在您的“主函数”中创建多个ManualResetEvent并将它们放入一个数组中,每个事件都“连接”到一个任务上,然后每个任务在完成时Set其事件。然后在您的“主函数”中执行以下操作:
WaitHandle.WaitAll(arrayOfManualResetEvents);

请注意,通过这种方式您最多可以等待64个事件。如果需要更多,则有另一种方法(请注意,即使您在STA线程上,例如WinForm应用程序的主线程,也必须使用此最后一种方法)。
ManualResetEvent mre = new ManualResetEvent(false);
int remaining = xxx; // Number of "tasks" that must be executed.

// Launch tasks

mre.WaitOne();

每个任务结束时

if (Interlocked.Decrement(ref remaining) == 0)
{
    mre.Set();
}

只有最后一个任务会将剩余字段减少到0并调用mre.Set()


@NickButler 感谢您指出这一点。我已经忘记了它/我从来不需要这样做(我更喜欢使用 Interlocked.Decrement 方法)。 - xanatos
如果我有两个,我能不能只做 mre.WaitOne(); mre.WaitOne(); - Benjol
@Benjol 你需要两个 ManualResetEvent,所以使用 mre1.WaitOne(); mre2.WaitOne(); - xanatos
最终我采用了这个解决方案,因为我没有其他理由在另一个线程上运行 SetupNetwork - Benjol

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接