请帮助我使这段代码线程安全

我在使数据加载和过滤线程安全方面遇到了一些问题。

我的控件基类上的以下代码通过BackgroundWorker处理所有数据填充。 这往往会在“this.DataWorker.RunWorkerAsync()”上抛出错误,说明BackgroundWorker正忙。

///  /// Handles the population of the form data. ///  /// Whether to pull data back from the WebService. public void Populate(bool reload) { if (!this.DataWorker.IsBusy) { // Disable the filter options IvdSession.Instance.FilterManager.SetEnabledState(this.GetType(), false); // Perform the population this.DataWorker.RunWorkerAsync(reload); } else if (!reload) { // If the data worker is busy and this is a not reload, then something bad has happened (ie the filter has run during a reload.) throw new InvalidOperationException("The DataWorker was busy whilst asked to reload."); } } 

代码在两个可能的位置调用。 首先是控件所在表单上的计时器:

 private void tmrAutoRefresh_Tick(object sender, EventArgs e) { if (!(this.CurrentBody == null)) { this.CurrentBody.Populate(true); } } 

其次,只要用户从多个下拉列表中选择过滤选项,

 public void Filter() { if (!m_BlockFilter) { IvdInstance.Main.CurrentBody.FirstRun = true; IvdInstance.Main.CurrentBody.Populate(false); } } 

主窗体上的Timer每60秒运行一次,并将true传递给Populate方法。 将重新加载作为trues传递告诉BackgroundWorker它需要从WebService中提取一组新数据:

 void dataWorker_DoWork(object sender, DoWorkEventArgs e) { try { if (base.FirstRun) { base.CleanListView(); } if ((bool)e.Argument) { byte[] serialized = IvdSession.DataAccess.GetServiceCalls(IvdSession.Instance.Company.Description, IvdSession.Instance.Company.Password, null); m_DataCollection = new DalCollection(serialized); } List collection = this.ApplyFilter(); base.HandlePopulation(collection, e); } catch (WebException ex) { // Ignore - Thrown when user clicks cancel } catch (System.Web.Services.Protocols.SoapException ex) { // Log error on server and stay transparent to user base.LogError(ex); } catch (System.Data.SqlClient.SqlException ex) { // Inform user that the database is unavailable base.HandleSystemUnavailable(ex); } } 

据我所知,当我设法在Timer触发填充事件的同时单击filter选项时,会发生错误。 我认为Populate方法中缺少一些东西,即一个锁,但我不确定如何在这个实例中正确使用它。

该代码有利于用户输入。 如果用户选择filter选项,则应阻止自动更新,如果自动更新触发,则暂时禁用filter选项。 如果它们同时触发,则用户输入应优先(如果可能)。

希望有人可以帮忙!

首先,在Populate方法体周围添加一个锁:

 private object _exclusiveAccessLock = new object(); public void Populate(bool reload) { lock (_exclusiveAccessLock) { // start the job } } 

这将帮助您避免竞争条件( 尽管 :如果我做对了,因为您使用的是Windows.Forms Timer,它将始终从Gui线程触发,因此它们永远不应该在同一时间执行)。

接下来,我不确定你是否应该抛出exception。 例如,您可以设置一个额外的标志,向您显示工作人员尚未完成,但这正是IsBusy应该告诉您的。

然后是m_BlockFilter标志。 我无法看到你在哪里设置它。 它也应该设置在锁内,而不是在后台线程中,因为在这种情况下你不能确定它不会被延迟。 如果要将其用作跨线程标志,还需要使该字段变为volatile 。

请参见线程同步(C#编程指南) :

 public class TestThreading { private System.Object lockThis = new System.Object(); public void Function() { lock (lockThis) { // Access thread-sensitive resources. } } } 

编辑 :您不希望两个线程进入Populate,因此您可以执行以下操作:

 public void Populate(bool reload) { lock (lockThis) { // Disable the filter options IvdSession.Instance.FilterManager.SetEnabledState(this.GetType(), false); // do actual work. } } 

编辑2 :你对BackgroundWorker有好处,所以也许你可以做这样的事情让其他线程等待。

 public void Populate(bool reload) { while (this.DataWorker.IsBusy) { Thread.Sleep(100); } // Disable the filter options IvdSession.Instance.FilterManager.SetEnabledState(this.GetType(), false); // Perform the population this.DataWorker.RunWorkerAsync(reload); }