2009-07-03 43 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