我有一个BindingList数据绑定到datgridview。我用它来跟踪一些实时价格。各种线程每秒多次调用方法'update(Quote quote)'。如果datagridview不包含Quote,则添加它。如果是,则更新报价的值。我不希望在BindingList(或GUI)中出现两次相同的引用,所以我试图锁定检查值是否在列表中的操作。它不起作用!我究竟做错了什么?我尝试了两种不同的锁定方式,并锁定了一个String对象,而不仅仅是一个对象。问题肯定在BeginInvoke(new MethodInvoker(delegate() { activeQuotes.Insert(0, quote); }));
调用(这可能需要一些时间),但如果我将其设为同步,则'add'方法会抛出'交叉线程'错误。 。 。我该怎么做才能避免交叉线程错误,但确保锁也可以工作?
public BindingList<Quote> activeQuotes = new BindingList<Quote>();
object lockObject = "lockObject";
dataGridViewActive.DataSource = activeQuotes;
public void update(Quote quote)
{
//lock (lockObject)
if(Monitor.TryEnter(lockObject))
{
try
{
if (!activeQuotes.Contains(quote))
{
try
{
activeQuotes.Add(quote);
AddQuote(quote);
}
catch (Exception ex)
{
Console.WriteLine("Datagridview!!!!!!");
}
}
else
{
int index = activeQuotes.IndexOf(quote);
activeQuotes[index].Bid = quote.Bid;
activeQuotes[index].Ask = quote.Ask;
activeQuotes[index].Mid = quote.Mid;
activeQuotes[index].Spread = quote.Spread;
activeQuotes[index].Timestamp = quote.Timestamp;
}
finally
{
Monitor.Exit(lockObject);
}
}
private void AddQuote(Quote quote)
{
if (this.InvokeRequired)
{
BeginInvoke(new MethodInvoker(delegate() { activeQuotes.Insert(0, quote); }));
BeginInvoke(new MethodInvoker(delegate() { dataGridViewActive.Refresh(); }));
BeginInvoke(new MethodInvoker(delegate() { dataGridViewActive.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells); }));
}
else
{
activeQuotes.Add(quote);
dataGridViewActive.Refresh();
dataGridViewActive.AutoResizeColumns (DataGridViewAutoSizeColumnsMode.AllCells);
}
}
我对此感到非常感激。
感谢。
答案 0 :(得分:7)
我之前写的这段代码会做
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
BindingListInvoked<Name> names;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
names = new BindingListInvoked<Name>(dataGridView1);
dataGridView1.DataSource = names;
new Thread(() => names.Add(new Name() { FirstName = "Larry", LastName = "Lan" })).Start();
new Thread(() => names.Add(new Name() { FirstName = "Jessie", LastName = "Feng" })).Start();
}
}
public class BindingListInvoked<T> : BindingList<T>
{
public BindingListInvoked() { }
private ISynchronizeInvoke _invoke;
public BindingListInvoked(ISynchronizeInvoke invoke) { _invoke = invoke; }
public BindingListInvoked(IList<T> items) { this.DataSource = items; }
delegate void ListChangedDelegate(ListChangedEventArgs e);
protected override void OnListChanged(ListChangedEventArgs e)
{
if ((_invoke != null) && (_invoke.InvokeRequired))
{
IAsyncResult ar = _invoke.BeginInvoke(new ListChangedDelegate(base.OnListChanged), new object[] { e });
}
else
{
base.OnListChanged(e);
}
}
public IList<T> DataSource
{
get
{
return this;
}
set
{
if (value != null)
{
this.ClearItems();
RaiseListChangedEvents = false;
foreach (T item in value)
{
this.Add(item);
}
RaiseListChangedEvents = true;
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
}
}
}
public class Name
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
答案 1 :(得分:2)
我认为您应该将BeginInvoke
更改为Invoke
。您需要在UI线程上获取它,而不是开始异步操作。否则,在调用BeginInvoke
目标之前,您的锁可能会被释放,因为在调用BeginInvoke
后会立即返回控制权。调用Invoke
将阻止该调用上的该线程,直到Invoke目标完成,然后将控制权返回给您的线程,这将确保保持锁定。
另外,您是否考虑使用lock
块而不是Monitor
方法调用?它基本上是相同的,但阻止你需要try / finally。我没有看到您正在使用任何重试或从TryEnter
中受益,但代码示例可能没有证明这一点。