简短版本:
我需要在线程之间来回传递复杂数据,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'的一个分支上,我应该遵循这个模式,它是什么用的?
答案 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)线程上时,你可以跳过它。