如何修复我的后台解决方案以进行线程安全调用?
How to fix my Background solution to make thread-safe calls?
我试图从另一个线程设置 TextBox 的 Text 属性。我在下面得到了这个例外;
"Cross-thread operation not valid: Control 'recTpcTxt' accessed from a thread other than the thread it was created on."
然后,我使用BackgroundWorker解决了这个问题。但是,我遇到了相同的异常消息。
编辑[1]:
其实这次是我自己带导游的link; https://msdn.microsoft.com/en-us/library/ms171728(v=vs.110).aspx。我可以使用 invoke属性 来解决我的问题。但是,我无法使用 backgroundworker 解决我的问题。
我的解决方案有问题吗?如何修复我的解决方案以设置一些 属性 of UI 变量?
编辑[2]: 更多代码来澄清问题;
MqttManager.cs;
public partial class MqttManager : Form
{
MqttHandler mqttHandler = new MqttHandler();
public static MqttManager managerInst;
public MqttManager()
{
InitializeComponent();
managerInst = this;
...
}
...
private BackgroundWorker backgroundWorker;
public void NotifyUIForRecMsg(string topic, string message)
{
object[] objArr = { topic, message };
this.backgroundWorker.RunWorkerAsync(objArr);
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
System.Threading.Thread.Sleep(5000);
e.Result = e.Argument;
}
private void backgroundWorker_RunWorkerCompleted(
object sender,
RunWorkerCompletedEventArgs e)
{
object[] res = (object[])e.Result;
this.recTpcTxt.Text = (String)res[0];
}
}
MqttManager.Design.cs;
partial class MqttManager
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
...
this.backgroundWorker = new System.ComponentModel.BackgroundWorker();
this.backgroundWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker_DoWork);
this.backgroundWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker_RunWorkerCompleted);
}
#endregion
...
}
MqttHandler.cs;
class MqttHandler
{
MqttClient client;
...
/// <summary>
/// Publish received event handler.
/// </summary>
private void client_MqttMsgPublishReceived(Object sender, MqttMsgPublishEventArgs e)
{
MqttManager.managerInst.NotifyUIForRecMsg(e.Topic, Encoding.UTF8.GetString(e.Message));
}
}
检查这个:
https://msdn.microsoft.com/en-us/library/ms171728(v=vs.110).aspx
基本上,要设置控件属性,您必须在同一个 UI 线程中。
这个简单的解决方案将调用移至 UI 线程
中的 textbox1.Text = someText
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
}
此外,您可以使用 textBox1.BeginInvoke
而不是 Invoke
:它会 运行 在 UI 线程中,而不锁定等待 SetText
的调用线程代表待完成
[编辑]在你的 backgroundWorker 中完成:
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
object[] arg = (object[])e.Argument;
SetTextToTextBox(recTpcTxt, (string)arg[0]);
SetTextToTextBox(recMsgTxt, (string)arg[1]);
}
private void SetTextToTextBox(TextBox toSet, string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (toSet.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
toSet.Invoke(d, new object[] { text });
}
else
{
toSet.Text = text;
}
}
[编辑 2]
正确使用 backgroundworker
注册活动 DoWork
和 RunWorkerCompleted
this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);
在退出backgroundWorker1_DoWork
之前,设置eventArgs的结果属性,并在backgroundWorker1_RunWorkerCompleted
中读取它们
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
System.Threading.Thread.Sleep(5000);
e.Result = new string[] { "one", "two" };
}
private void backgroundWorker1_RunWorkerCompleted(
object sender,
RunWorkerCompletedEventArgs e)
{
string[] res = (string[])e.Result;
this.textBox1.Text = res[0];
}
我试图从另一个线程设置 TextBox 的 Text 属性。我在下面得到了这个例外;
"Cross-thread operation not valid: Control 'recTpcTxt' accessed from a thread other than the thread it was created on."
然后,我使用BackgroundWorker解决了这个问题。但是,我遇到了相同的异常消息。
编辑[1]: 其实这次是我自己带导游的link; https://msdn.microsoft.com/en-us/library/ms171728(v=vs.110).aspx。我可以使用 invoke属性 来解决我的问题。但是,我无法使用 backgroundworker 解决我的问题。
我的解决方案有问题吗?如何修复我的解决方案以设置一些 属性 of UI 变量?
编辑[2]: 更多代码来澄清问题;
MqttManager.cs;
public partial class MqttManager : Form
{
MqttHandler mqttHandler = new MqttHandler();
public static MqttManager managerInst;
public MqttManager()
{
InitializeComponent();
managerInst = this;
...
}
...
private BackgroundWorker backgroundWorker;
public void NotifyUIForRecMsg(string topic, string message)
{
object[] objArr = { topic, message };
this.backgroundWorker.RunWorkerAsync(objArr);
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
System.Threading.Thread.Sleep(5000);
e.Result = e.Argument;
}
private void backgroundWorker_RunWorkerCompleted(
object sender,
RunWorkerCompletedEventArgs e)
{
object[] res = (object[])e.Result;
this.recTpcTxt.Text = (String)res[0];
}
}
MqttManager.Design.cs;
partial class MqttManager
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
...
this.backgroundWorker = new System.ComponentModel.BackgroundWorker();
this.backgroundWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker_DoWork);
this.backgroundWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker_RunWorkerCompleted);
}
#endregion
...
}
MqttHandler.cs;
class MqttHandler
{
MqttClient client;
...
/// <summary>
/// Publish received event handler.
/// </summary>
private void client_MqttMsgPublishReceived(Object sender, MqttMsgPublishEventArgs e)
{
MqttManager.managerInst.NotifyUIForRecMsg(e.Topic, Encoding.UTF8.GetString(e.Message));
}
}
检查这个: https://msdn.microsoft.com/en-us/library/ms171728(v=vs.110).aspx
基本上,要设置控件属性,您必须在同一个 UI 线程中。
这个简单的解决方案将调用移至 UI 线程
中的textbox1.Text = someText
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
}
此外,您可以使用 textBox1.BeginInvoke
而不是 Invoke
:它会 运行 在 UI 线程中,而不锁定等待 SetText
的调用线程代表待完成
[编辑]在你的 backgroundWorker 中完成:
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
object[] arg = (object[])e.Argument;
SetTextToTextBox(recTpcTxt, (string)arg[0]);
SetTextToTextBox(recMsgTxt, (string)arg[1]);
}
private void SetTextToTextBox(TextBox toSet, string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (toSet.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
toSet.Invoke(d, new object[] { text });
}
else
{
toSet.Text = text;
}
}
[编辑 2] 正确使用 backgroundworker
注册活动 DoWork
和 RunWorkerCompleted
this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);
在退出backgroundWorker1_DoWork
之前,设置eventArgs的结果属性,并在backgroundWorker1_RunWorkerCompleted
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
System.Threading.Thread.Sleep(5000);
e.Result = new string[] { "one", "two" };
}
private void backgroundWorker1_RunWorkerCompleted(
object sender,
RunWorkerCompletedEventArgs e)
{
string[] res = (string[])e.Result;
this.textBox1.Text = res[0];
}