在c#winforms应用程序的线程之间传递数据

时间:2014-01-21 13:40:02

标签: c# multithreading winforms

简短版本:

我需要在线程之间来回传递复杂数据,1个线程是WinForm,另一个线程调用在线翻译服务并更改Winform使用的所有数据。

长版:

将大量数据传递给在线翻译服务一次冻结我的前端几分钟,所以我试图将该逻辑移动到一个线程中。 Winform广泛使用在线服务需要处理的数据,并附带新的信息返回。

我正在使用此代码启动主题:

threadWork tw = new threadWork();  //obj to hold data for thread
tw.settings = frmMain.projectSettings; 
tw.CompletedEvent += tw_CompletedEvent;   // callback event, returns data
ThreadPool.QueueUserWorkItem(doWork,tw);  // kick off thread

接收回调码:

void tw_CompletedEvent(projectFormat settings)
{
    frmMain.projectSettings = settings;
    NotifyLoadTransationKeys(frmMain.projectSettings.translationKeys,frmMain.projectSettings.translatedLanguages);

}

基本上造成了这个错误:

Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on.  [on frmMain]

所以我发现this建议在Program.cs中使用[STAThread] [sta =单线程公寓](默认c#winform入口点)

using System.Threading;
Thread t = new Thread(new ThreadStart(StartNewStaThread));
// Make sure to set the apartment state BEFORE starting the thread. 
t.ApartmentState = ApartmentState.STA; 
t.Start(); 
private void StartNewStaThread() { 
    Application.Run(new Form1()); 
}

我的C#应用​​程序中的所有内容都以'Program.cs'开头,所以我尝试了上述建议:

static class Program
{
    public static translationUtil TranslationUtil;  //location of time intensive webservice
    public static projectFormat projectSettings; //data that needs sharing

    static void Main() // prog entry point
    {            
       ProgramCode pc = new ProgramCode();
       pc.Main();
    }
}

class ProgramCode
{
    public void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);            

        Thread t = new Thread(new ThreadStart(StartNewStaThread));
        t.ApartmentState = ApartmentState.STA;
        t.Start();
    }

    private void StartNewStaThread()
    {
        Application.Run(new Main());
    }
}

在上面的代码中我试图将共享资源移动到'Program.cs',然后创建了一个新的类和线程来保存我的winform,但这仍然给我提供了交叉线程问题(与之前相同的错误)!在我的情况下,有没有人建议如何使用线程并成功共享数据?

更新:@HenkHolterman到目前为止最好回答了我的问题,但在研究了他给我的答案后,我多次遇到过这段代码(使用“调用”)

if (control.InvokeRequired) {
        control.Invoke(new ControlStringConsumer(SetText), new object[]{control, text});  // invoking itself
    } else {
        control.Text=text;      // the "functional part", executing only on the main thread
    }

关注问题......上面的'if'语句似乎只是在没有调用的情况下设置文本(在'if'的一个分支上,我应该遵循这个模式,它是什么用的?

1 个答案:

答案 0 :(得分:1)

您的基本解决方案:

void tw_CompletedEvent(projectFormat settings)
{
    frmMain.projectSettings = settings;
    frmMain.Invoke( () =>
       NotifyLoadTransationKeys(
           frmMain.projectSettings.translationKeys,
           frmMain.projectSettings.translatedLanguages)
     );

}

但是当NotifyLoadTransationKeys()花费大量时间时,您需要将其分解。

编辑:

调用Invoke()通常包含if (control.InvokeRequired),这是一个小优化。但是当你确定你在另一个(而不是UI)线程上时,你可以跳过它。