2009-07-03 19 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メソッドにtrueを渡します。 truesとしてリロードを渡すと、Webサービスからのデータの新鮮なセットをプルダウンする必要があるとの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タイマー、それは常にGuiスレッドから発射されるので、決して実行されるべきではありません。正確にはです。

次に、例外をスローする必要があるかどうかはわかりません。たとえば、作業者がまだ完了していないことを示す追加のフラグを設定することができますが、それはとにかくIsBusyに通知する必要があります。

次に、m_BlockFilterフラグがあります。私はあなたがそれをどこから設定しているのか分かりません。バックグラウンドスレッドではなく、ロック内にも設定する必要があります。その場合、遅延が発生しないようにすることはできません。また、クロススレッドフラグとして使用する場合は、フィールドvolatileを作成する必要があります。

+0

@Groo、Windowsの正確なスペックは分かっていませんが、マルチコアでは同時に2つのことを実行できませんか? –

+0

はい、メソッド*はスレッドセーフにする必要があります。しかしGuiスレッドからGui要素を常に更新する必要があるという規則があります。したがって、Windows.Forms.TimerはイベントハンドラをGuiスレッドキュー(Guiスレッドから呼び出す)に追加して処理を簡素化します。 – Groo

+0

「Windowsの正確な仕様を知らない」と冗談を言っているのは誰ですか? :) – Groo

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. 
     } 
    } 
} 

編集:あなたが怒鳴るような何かができるようにするには、2つのスレッドが移入を入力する必要はありません:

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

関連する問題