在Timer中使用QueueUserWorkItem时出现ThreadStateException

时间:2010-06-07 14:19:19

标签: c# winforms multithreading clipboard

我的WinForms应用程序中有一个ThreadStateException。

重现步骤:

  • 创建简单的winforms app
  • 添加计时器
  • 在点击事件中,请执行:

    timer1.Interval = 1000;
    timer1.Tick += timer1_Tick;
    timer1.Start();
    

    void timer1_Tick(object sender, EventArgs e)
    {
        ThreadPool.QueueUserWorkItem(delegate
        {
            StringCollection paths =
                new StringCollection { @"c:\my.txt", @"c:\my.png" };
            Clipboard.SetFileDropList(paths);
        });
    }
    

例外告诉我:

  

在进行OLE调用之前,必须将当前线程设置为单线程单元(STA)模式。确保您的Main函数标记了STAThreadAttribute。

但主要已经有[STAThread]属性。

如何解决?

1 个答案:

答案 0 :(得分:2)

Thread.SetApartmentState()方法在这里很重要。剪贴板是一个COM对象,它不是线程安全的。有许多Windows功能,其行为与此类似,Drag + Drop和OpenFileDialog等shell对话框是其他示例。

你不能设置线程池线程的公寓状态,它总是MTA(多线程公寓)。您可以在常规线程上,但是额外的STA要求是您还要泵送消息循环(Application.Run)。这给了你完全相同的问题:你不能阻止或花很长时间来执行代码。

简单的解决方案是在线程上执行任何需要很长时间才能执行的操作。并从UI线程进行剪贴板调用。使用Control.BeginInvoke()或更好的BackgroundWorker.RunWorkerCompleted。