来自表单滑块 (TrackBar) 的线程安全 'get'
Threadsafe 'get' from form slider (TrackBar)
我有一个应用程序,其初始表单具有各种配置(复选框、文本框、sliders/trackbars)。当我启动使用这些配置的操作时,有一个名为的工作进程:
private void btnx_Click(object sender, EventArgs e)
{
bgWorkerX.RunWorkerAsync();
}
private void bgWorkerX_DoWork(object sender, DoWorkEventArgs e)
{
Dictionary<string, object> dictCfgOut = GenerateConfigDict();
XGen.CreateX.aReadFile(inputPathXRAW.Text, outputPathX.Text, dictCfgOut);
}
GenerateConfigDict 函数创建 Dictionary<string, object>
配置数据。这是因为所需的数据各不相同(一些布尔值、一些整数、一个字符串数组)。创建它的代码如下:
private Dictionary<string, object> GenerateConfigDict()
{
Dictionary<string, object> ConfigDict = new Dictionary<string, object>();
if (cbXAddOnus.Checked)
{
if (!MiscTools.ValidateX(txtInstX.Text, true))
return null;
}
ConfigDict.Add("Available Images", GenerateConfigImageArray());
ConfigDict.Add("X SCSC Hot", (cbXAddSCSC.Checked) ? (bool)true : (bool)false);
ConfigDict.Add("X Req Adj", (cbXAddAdjustment.Checked) ? (bool)true : (bool)false);
ConfigDict.Add("X Items", (cbXAddX.Checked) ? (bool)true : (bool)false);
ConfigDict.Add("X Use Something", (cbXAddX.Checked) ? (bool)true : (bool)false);
ConfigDict.Add("X Somedata", (string)txtX.Text);
ConfigDict.Add("X Use SAN", (cbX.Checked) ? (bool)true : (bool)false);
ConfigDict.Add("X % Y", (int)sliderX.Value);
return ConfigDict;
}
sliderX.Value 部分是今天刚刚添加的。在此之前,一切都很好。然而,只有那一点,我得到:
Cross-thread operation not valid: Control 'sliderX' accessed from a
thread other than the thread it was created on.
首先,为什么读取该值不是线程安全的,但读取复选框和文本是线程安全的?其次,我一直在研究使该线程安全的问题,但这似乎都需要另一个函数来异步调用该值。我已经采取措施确保在工作人员 运行 时禁用滑块,因此无法从表单进行更改。
谢谢!
控件的线程行为与 .NET 包装的非托管代码有关。我不确切知道哪个 属性 可以 and/or 应该 从任何线程访问以及为什么。
我不会使用 Invoke
回调 UI 线程,除非您需要 "real time" 访问控件(即更新 ProgressBar
值) .相反,在 运行 后台工作者之前,在 UI 线程上收集 UI 东西并将它们提供给工作者:
bgWorkerX.RunWorkerAsync((int)sliderX.Value);
...
private void bgWorkerX_DoWork(object sender, DoWorkEventArgs e)
{
Dictionary<string, object> dictCfgOut = GenerateConfigDict((int)e.Argument);
...
}
private Dictionary<string, object> GenerateConfigDict(int sliderValue)
{
...
ConfigDict.Add("X % Y", sliderValue);
...
}
除了这个值,您甚至可以选择在 UI 线程上创建整个配置字典,然后将其用作后台工作程序的支持。
要线程安全地访问任何控件,您应该调用该控件的 Invoke() 方法 (MSDN tutorial)。
因此,您的代码将如下所示:
delegate int GetSliderValueCallback();
private int GetValue()
{
int sliderValue;
if (sliderX.InvokeRequired)
{
GetSliderValueCallback cb = new GetSliderValueCallback(GetValue);
return (int)sliderX.Invoke(cb);
}
else
{
return (int)sliderX.Value;
}
}
并在读取值时调用它:
ConfigDict.Add("X % Y", GetValue());
我也建议您使用 async methods。
private async void btnx_Click(object sender, EventArgs e)
{
await bgWorkerX_DoWorkAsync();
}
private async void bgWorkerX_DoWorkAsync()
{
Dictionary<string, object> dictCfgOut = await GenerateConfigDictAsync();
XGen.CreateX.aReadFile(inputPathXRAW.Text, outputPathX.Text, dictCfgOut);
}
private Task<Dictionary<string, object>> GenerateConfigDictAsync()
{
return Task.Run(() => {
Dictionary<string, object> ConfigDict = new Dictionary<string, object>();
if (cbXAddOnus.Checked)
{
if (!MiscTools.ValidateX(txtInstX.Text, true))
return null;
}
ConfigDict.Add("Available Images", GenerateConfigImageArray());
ConfigDict.Add("X SCSC Hot", (cbXAddSCSC.Checked) ? (bool)true : (bool)false);
ConfigDict.Add("X Req Adj", (cbXAddAdjustment.Checked) ? (bool)true : (bool)false);
ConfigDict.Add("X Items", (cbXAddX.Checked) ? (bool)true : (bool)false);
ConfigDict.Add("X Use Something", (cbXAddX.Checked) ? (bool)true : (bool)false);
ConfigDict.Add("X Somedata", (string)txtX.Text);
ConfigDict.Add("X Use SAN", (cbX.Checked) ? (bool)true : (bool)false);
ConfigDict.Add("X % Y", (int)sliderX.Value);
return ConfigDict;
});
}
警告! 不幸的是我没有机会构建该代码(我只是在这里写的),所以,请使用它 作为示例.
我有一个应用程序,其初始表单具有各种配置(复选框、文本框、sliders/trackbars)。当我启动使用这些配置的操作时,有一个名为的工作进程:
private void btnx_Click(object sender, EventArgs e)
{
bgWorkerX.RunWorkerAsync();
}
private void bgWorkerX_DoWork(object sender, DoWorkEventArgs e)
{
Dictionary<string, object> dictCfgOut = GenerateConfigDict();
XGen.CreateX.aReadFile(inputPathXRAW.Text, outputPathX.Text, dictCfgOut);
}
GenerateConfigDict 函数创建 Dictionary<string, object>
配置数据。这是因为所需的数据各不相同(一些布尔值、一些整数、一个字符串数组)。创建它的代码如下:
private Dictionary<string, object> GenerateConfigDict()
{
Dictionary<string, object> ConfigDict = new Dictionary<string, object>();
if (cbXAddOnus.Checked)
{
if (!MiscTools.ValidateX(txtInstX.Text, true))
return null;
}
ConfigDict.Add("Available Images", GenerateConfigImageArray());
ConfigDict.Add("X SCSC Hot", (cbXAddSCSC.Checked) ? (bool)true : (bool)false);
ConfigDict.Add("X Req Adj", (cbXAddAdjustment.Checked) ? (bool)true : (bool)false);
ConfigDict.Add("X Items", (cbXAddX.Checked) ? (bool)true : (bool)false);
ConfigDict.Add("X Use Something", (cbXAddX.Checked) ? (bool)true : (bool)false);
ConfigDict.Add("X Somedata", (string)txtX.Text);
ConfigDict.Add("X Use SAN", (cbX.Checked) ? (bool)true : (bool)false);
ConfigDict.Add("X % Y", (int)sliderX.Value);
return ConfigDict;
}
sliderX.Value 部分是今天刚刚添加的。在此之前,一切都很好。然而,只有那一点,我得到:
Cross-thread operation not valid: Control 'sliderX' accessed from a thread other than the thread it was created on.
首先,为什么读取该值不是线程安全的,但读取复选框和文本是线程安全的?其次,我一直在研究使该线程安全的问题,但这似乎都需要另一个函数来异步调用该值。我已经采取措施确保在工作人员 运行 时禁用滑块,因此无法从表单进行更改。
谢谢!
控件的线程行为与 .NET 包装的非托管代码有关。我不确切知道哪个 属性 可以 and/or 应该 从任何线程访问以及为什么。
我不会使用 Invoke
回调 UI 线程,除非您需要 "real time" 访问控件(即更新 ProgressBar
值) .相反,在 运行 后台工作者之前,在 UI 线程上收集 UI 东西并将它们提供给工作者:
bgWorkerX.RunWorkerAsync((int)sliderX.Value);
...
private void bgWorkerX_DoWork(object sender, DoWorkEventArgs e)
{
Dictionary<string, object> dictCfgOut = GenerateConfigDict((int)e.Argument);
...
}
private Dictionary<string, object> GenerateConfigDict(int sliderValue)
{
...
ConfigDict.Add("X % Y", sliderValue);
...
}
除了这个值,您甚至可以选择在 UI 线程上创建整个配置字典,然后将其用作后台工作程序的支持。
要线程安全地访问任何控件,您应该调用该控件的 Invoke() 方法 (MSDN tutorial)。
因此,您的代码将如下所示:
delegate int GetSliderValueCallback();
private int GetValue()
{
int sliderValue;
if (sliderX.InvokeRequired)
{
GetSliderValueCallback cb = new GetSliderValueCallback(GetValue);
return (int)sliderX.Invoke(cb);
}
else
{
return (int)sliderX.Value;
}
}
并在读取值时调用它:
ConfigDict.Add("X % Y", GetValue());
我也建议您使用 async methods。
private async void btnx_Click(object sender, EventArgs e)
{
await bgWorkerX_DoWorkAsync();
}
private async void bgWorkerX_DoWorkAsync()
{
Dictionary<string, object> dictCfgOut = await GenerateConfigDictAsync();
XGen.CreateX.aReadFile(inputPathXRAW.Text, outputPathX.Text, dictCfgOut);
}
private Task<Dictionary<string, object>> GenerateConfigDictAsync()
{
return Task.Run(() => {
Dictionary<string, object> ConfigDict = new Dictionary<string, object>();
if (cbXAddOnus.Checked)
{
if (!MiscTools.ValidateX(txtInstX.Text, true))
return null;
}
ConfigDict.Add("Available Images", GenerateConfigImageArray());
ConfigDict.Add("X SCSC Hot", (cbXAddSCSC.Checked) ? (bool)true : (bool)false);
ConfigDict.Add("X Req Adj", (cbXAddAdjustment.Checked) ? (bool)true : (bool)false);
ConfigDict.Add("X Items", (cbXAddX.Checked) ? (bool)true : (bool)false);
ConfigDict.Add("X Use Something", (cbXAddX.Checked) ? (bool)true : (bool)false);
ConfigDict.Add("X Somedata", (string)txtX.Text);
ConfigDict.Add("X Use SAN", (cbX.Checked) ? (bool)true : (bool)false);
ConfigDict.Add("X % Y", (int)sliderX.Value);
return ConfigDict;
});
}
警告! 不幸的是我没有机会构建该代码(我只是在这里写的),所以,请使用它 作为示例.