【问题标题】:WaitAll for multiple handles on a STA thread is not supported不支持 STA 线程上的多个句柄的 WaitAll
【发布时间】:2010-11-16 09:50:30
【问题描述】:
  1. 为什么会收到此错误消息? “不支持 STA 线程上的多个句柄的 WaitAll。”
  2. 我应该使用 [MTAThreadAttribute] 属性吗? 更新:不适用于 WPF 应用程序!

注意: 错误在 WaitHandle.WaitAll(doneEvents); 我正在使用标准的 WPF 项目

private void Search()
{
    const int CPUs = 2;
    var doneEvents = new ManualResetEvent[CPUs];

    // Configure and launch threads using ThreadPool:
    for (int i = 0; i < CPUs; i++)
    {
        doneEvents[i] = new ManualResetEvent(false);
        var f = new Indexer(Paths[i], doneEvents[i]);
        ThreadPool.QueueUserWorkItem(f.WaitCallBack, i);
    }

    // Wait for all threads in pool 
    WaitHandle.WaitAll(doneEvents);
    Debug.WriteLine("Search completed!");
}

更新:以下解决方案不适用于 WPF 应用程序! 无法将主应用程序属性更改为 MTAThreadAttribute。这将导致以下错误:

错误:“不支持 STA 线程上的多个句柄的 WaitAll。”

【问题讨论】:

  • 您意识到您的应用程序将完全冻结,直到那些索引器完成?
  • 是的!我知道。但实际上它并没有运行。
  • 同样的错误,但解决方案不适用于 WPF 应用程序!您不能更改 WPF 应用程序的主要方法属性。

标签: c# multithreading threadpool


【解决方案1】:

其实我用下面的来代替 WaitHandle.WaitAll(doneEvents);

foreach (var e in doneEvents)
    e.WaitOne();

【讨论】:

  • 使用这种替代方法有什么缺点吗?
  • 我没见过,但我建议使用 Task 来获取全新的代码
  • 任务不能用于 e。 G。一个优雅的关闭实现,这就是为什么我喜欢下面 Brian Gideon 的答案 (stackoverflow.com/a/4194938/2477582),它建议使用 CountdownEvent
  • 如果这很简单,为什么微软没有在 WaitAll() 中实现它?阅读所有答案后,我仍然不明白愚蠢的错误信息。为什么微软告诉我们它不适用于 STA 线程?等待事件的类型与等待事件有什么关系?
【解决方案2】:

如何使用任务为您执行线程。

http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.aspx

var task1 = Task.Factory.StartNew(() => DoSomeWork());
var task2 = Task.Factory.StartNew(() => DoSomeWork());
var task3 = Task.Factory.StartNew(() => DoSomeWork());
Task.WaitAll(task1, task2, task3);

【讨论】:

  • 这是.NET Framework 4。所以我必须升级项目。这似乎是一种更好的方法!
  • Task.Run(()=&gt;DoSomeWork()) 是更好的选择
【解决方案3】:

使用一个ManualResetEvent 并等待它。还要维护一个 TaskCount 变量,该变量设置为您启动的工作线程数,在工作线程代码中使用 Interlocked.Decrement 作为工作线程的最后一个操作,并在计数器达到零时发出事件信号,例如

// other worker actions...
if (Interlocked.Decrement(ref taskCount) == 0)
   doneEvent.Set();

【讨论】:

  • 谢谢,这似乎是唯一的解决方案,因为 WaitAll(..) 不起作用。
  • 是的,那行得通。只需确保将主线程视为工作项并初始化taskCount = 1 并在for 循环的末尾执行DecrementSet
  • 这与使用 CountdownEvent 非常相似。但是 CountdownEvent 的优点是您只需要一个变量而不是解决方案中的两个变量。
【解决方案4】:

我会重构您的代码以改用CountdownEvent 类。

private void Search() 
{ 
    const int CPUs = 2; 
    var done = new CountdownEvent(1);

    // Configure and launch threads using ThreadPool: 
    for (int i = 0; i < CPUs; i++) 
    { 
        done.AddCount();
        var f = new Indexer(Paths[i], doneEvents[i]); 
        ThreadPool.QueueUserWorkItem(
          (state) =>
          {
            try
            {
              f.WaitCallBack(state);
            }
            finally
            {
              done.Signal();
            }
          }, i); 
    } 

    // Wait for all threads in pool  
    done.Signal();
    done.Wait();
    Debug.WriteLine("Search completed!"); 
} 

【讨论】:

  • 这是WaitHandle.WaitAll(multipleEvents);的完美替代品!
【解决方案5】:

使用这样的东西:

foreach (ITask Task in Tasks)
{
    Task.WaitHandle = CompletedEvent;
    new Thread(Task.Run).Start();
}

int TasksCount = Tasks.Count;
for (int i = 0; i < TasksCount; i++)
    CompletedEvent.WaitOne();

if (AllCompleted != null)
    AllCompleted(this, EventArgs.Empty);

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-07
  • 1970-01-01
  • 2011-07-03
  • 2023-03-26
  • 2012-02-27
相关资源
最近更新 更多