2009-07-03 80 views
2

我已经有点让我的数据加载和过滤线程安全的问题。请帮我做这段代码线程安全

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

/// <summary> 
/// Handles the population of the form data. 
/// </summary> 
/// <param name="reload">Whether to pull data back from the WebService.</param> 
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 (i.e. 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); 
    } 
} 

的计时器主窗体每60秒运行一次并传递给Populate方法。传递重载为trues告诉它需要从WebService拉下一组新数据的BackgroundWorker:

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<ServiceCallEntity>(serialized); 
     } 

     List<ServiceCallEntity> collection = this.ApplyFilter(); 
     base.HandlePopulation<ServiceCallEntity>(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); 
    } 

} 

据我所知,当我设法单击完全是一个过滤器选项时发生错误定时器同时触发人口事件。我认为从Populate方法中缺少一些东西,即一个锁,但我不确定如何在这种情况下正确使用它。

该代码对用户输入有利。如果用户选择过滤器选项,则应禁止自动更新,如果自动更新触发,则临时禁用过滤器选项。如果他们同时启动,则用户输入应优先(如果可能)。

希望有人能帮助!

回答

2

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

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

这将帮助你避免出现竞争状况(虽然:如果我这样做是正确的,因为你使用的是Windows .Forms Timer,它会一直从Gui线程触发,所以它们不应该同时执行,正好是)。

接下来,我不确定是否应该抛出异常。例如,你可以设置一个额外的标志,告诉你工人还没有完成,但这就是IsBusy应该告诉你的。

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

+0

@Groo,不知道Windows的确切规格,但是对于多内核,你不能同时运行两件事吗? – 2009-07-03 18:30:19

1

Thread Synchronization (C# Programming Guide)

public class TestThreading 
{ 
    private System.Object lockThis = new System.Object(); 

    public void Function() 
    { 

     lock (lockThis) 
     { 
      // Access thread-sensitive resources. 
     } 
    } 
} 

编辑:你不想两个线程进入填充,所以你可以做一些事情波纹管:

public void Populate(bool reload) 
{ 

    lock (lockThis) 
    { 
     // Disable the filter options 
     IvdSession.Instance.FilterManager.SetEnabledState(this.GetType(), false); 

     // do actual work. 
    } 

} 

EDIT2:你有良好使用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); 
} 
+0

在上述代码的上下文中,我会在哪里使用它? – GenericTypeTea 2009-07-03 18:24:44