我在主UI中有一个组合框,我将显示一些可用的项目列表。我有一个后台工作线程,它将运行时间检查可用项目。
我在循环中运行此线程,以便每次检查可用项目并更新到主UI组合框。
这是我的代码段。
do
{
iNumberofReaders = 0;
this.Dispatcher.BeginInvoke((Action)(() =>
{
NumberOfCards.Items.Clear();
}));
//
// Compose a list of the card readers which are connected to the
// system and which will be monitored.
//
ArrayList availableReaders = this.ListReaders();
this._states = new ReaderState[availableReaders.Count];
for (int i = 0; i <= availableReaders.Count - 1; i++)
{
this._states[i].Reader = availableReaders[i].ToString();
}
result = (SmartcardErrorCode)UnsafeNativeMethods.GetStatusChange(
this._context, 1000, this._states, this._states.Length);
szAvailableReaders = new string[availableReaders.Count];
for (int i = 0; i < availableReaders.Count; i++)
{
if (0 != this._states[i]._attribute)
{
szAvailableReaders[iNumberofReaders] = this._states[i]._reader;
iNumberofReaders++;
} // if
} // for
if (iNumberofReaders > 1)
{
this.Dispatcher.BeginInvoke((Action)(() =>
{
for (int j = 0; j < iNumberofReaders; j++)
{
NumberOfCards.Visibility =system.Windows.Visibility.Visible;
NumberOfCards.Items.Add(szAvailableReaders[j]);
} // for
}));
}
} while (SetThread);
但这个代码在第二次迭代中抛出
对象引用未设置为对象的实例
发生了'System.NullReferenceException'类型的未处理异常
如果我评论组合框添加元素,那么它的工作正常。
添加元素中的常量字符串也会抛出相同的错误。所以我相信问题是for循环和调度员begininvoke。
NumberOfCards.Items.Add("ABCD");
(NumberOfCards是ComboBox)
我在调度程序开始调用中观察到for循环中的奇怪行为。 有人可以帮忙吗?
答案 0 :(得分:0)
您正在单独的线程中修改szAvailableReaders
数组,同时在GUI线程中读取它。
首先,您应该考虑使用Invoke
而不是BeginInvoke
,因为后者会在GUI线程上对请求进行排队,并且可以轻松创建一系列不必要地占用CPU的排队操作。
此外,在线程之间共享数据时最简单的做法是避免共享同一个实例,而是为UI线程创建数据的不可变副本。这也适用于iNumberofReaders
以及您在线程之间共享的任何其他字段。
换句话说,背景循环应该类似于:
do
{
var availableReaders = this.ListReaders();
// If you're only using `states` in this method,
// make it a local variable:
var states = availableReaders
.Select(r => new ReaderState() { Reader = r.ToString() })
.ToArray();
// fill the data
result = (SmartcardErrorCode)UnsafeNativeMethods
.GetStatusChange(this._context, 1000, states, states.Length);
// this is the important part: create a new instance of reader names.
// LINQ also simplifies it a bit:
var availableReaders = states
.Where(s => s._attribute != 0)
.Select(s => s._reader)
.ToList();
// now that you have a local list, you can simply swap the field
// which is referencing it (since object assignments are atomic in .NET).
// So the field gets a unique copy every time, and you can even go
// an extra step forward by declaring it as a `ReadOnlyCollection<string>`:
szAvailableReaders = new ReadOnlyCollection<string>(availableReaders);
// Consider using `Invoke` here:
this.Dispatcher.BeginInvoke((Action)(() =>
{
NumberOfCards.Items.Clear();
NumberOfCards.Items.AddRange(szAvailableReaders.ToArray());
}));
} while (SetThread);
除此之外,这看起来像一个非常紧凑的循环。我不确定你在这里读到什么,但是你不应该使用某种“读者列表更改”事件,只在这种情况下更新组合?或者至少使用计时器并每隔一秒左右轮询列表?