2011-07-06 4 views
2

私のアプリケーションはライブフィードからデータを取り出し、処理して結果を表示します。このデータは5秒ごとに更新されます。メインフォームのLoadイベントでは、最初のデータサイクルが実行されるまで表示されるスプラッシュ画面を表示するスレッドを作成しました。すべてのデータを取得する前にフォームが開きます。 UIスレッドが処理スレッドの最初のサイクルを待つようにするには?

データフェッチおよび処理スレッド(RecieverThread)がRecieveFeedを呼び出します。私が直面しているのは、最初のサイクルが完全に実行される前に、RecieveFeedでフェッチされたデータを表示するform2が表示されているということです。最初のサイクルがデータのフェッチを完了した後にのみ、form2がロードされるようにするにはどうすればよいですか。メインフォームで

コード:

private void frmMain_Load(object sender, EventArgs e) 
    { 
     Hide(); 

     // Create a new thread from which to start the splash screen form 
     Thread splashThread = new Thread(new ThreadStart(StartSplash)); 
     splashThread.Start(); 

    //Thread to call the live feed engine. This thread will run for the duration of application 
     ReceiverThread = new System.Threading.Thread(new System.Threading.ThreadStart(ReceiveFeed)); 
     ReceiverThread.Start(); 
     frmSecondForm form2 = new frmSecondForm(); 
     form2.MdiParent = this; 
     form2.WindowState = FormWindowState.Maximized; 
     Show(); 
     form2.Show(); 
    } 

    public frmRaceRace() 
    { 
     InitializeComponent(); 
     this.splash = new SplashScreen(); 
    } 

    private void StartSplash() 
    { 
     splash.Show(); 
     while (!done) 
     { 
      Application.DoEvents(); 
     } 
     splash.Close(); 
     this.splash.Dispose(); 

    } 

    private void ReceiveFeed() 
    { 

     while (!StopReceivingData)      
     { 
     foreach (...) 
     { 
     //Fetches data from live engine 
     DLLImportClass.GetData1(); 
     //Manipulates and stores the data fetched in datatables        
     ThreadPool.QueueUserWorkItem(new WaitCallback(delegate { StoreData(); })) 
     rowsProcessed++; 
     if (!done) 
     { 
      this.splash.UpdateProgress(100 * rowsProcessed/totalRows); 
     } 
     } 

     done = true;   
     Thread.Sleep(5000); 
    } 
    } 

答えて

3

私は何をここで使用する必要があることはSystem.Threading.AutoResetEventだと思います。基本的には、フォームクラスにこのメンバーを追加します。あなたのスプラッシュを表示した後

private AutoResetEvent waitEvent_ = new AutoResetEvent(false); // create unininitialized 

、あなたが合図するために、このイベントのために待ちたい:

private void StartSplash() 
{ 
    splash.Show(); 
    // this will time out after 10 seconds. Use WaitOne() to wait indefinitely. 
    if(waitEvent_.WaitOne(10000)) 
    { 
     // WaitOne() returns true if the event was signalled. 
    } 

} // eo StartSplash 

最後に、あなたの処理機能で、ときあなたは完了です。

waitEvent_.Set(); 
+0

これは、すべてのデータを取得する前にフォームが開くという問題を解決しました。 – Arcturus

0
はこれにあなたの frmMain_Loadを変更

private void frmMain_Load(object sender, EventArgs e) 
    { 
     Hide(); 

    //Thread to call the live feed engine. This thread will run for the duration of application 
     ReceiverThread = new System.Threading.Thread(new System.Threading.ThreadStart(ReceiveFeed)); 
     ReceiverThread.Start(); 
     frmSecondForm form2 = new frmSecondForm(); 
     form2.MdiParent = this; 
     form2.WindowState = FormWindowState.Maximized; 
     StartSplash(); 
     Show(); 
     form2.Show(); 
    } 
1

あなたのコードにいくつかの競合状態があるようです。

WinFormsとほとんどのUIフレームワークでスレッド化を行う場合、UIオブジェクト(フォームとコントロール)は1つのスレッドからのみアクセスできます。

他のすべてのスレッドは、.InvokeRequired()および.BeginInvoke()を使用してそのスレッドにのみアクセスできます。これらの呼び出しは、UIスレッドでデリゲートを実行するために使用できます。参照:

{更新済み:StackOverflowは1つだけのハイパーリンクを投稿できるようにします。 Googleこれら}}

BackgroundWorkerクラスには、このためのショートカットがあります。

http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx

単純に、この(擬似コード)を実行します。

public void StartSplash() 
{ 
    Splash.Show(); 
    BackgroundWorker bgw = new BackgroundWorker(); 
    // set up bgw Delegates 
    bgw.RunWorkerAsync(); 
} 

public void bgw_DoWork(... etc 
{ 
    // do stuff in background thread 
    // you cannot touch the UI from here 
} 

public void bgw_RunWorkerCompleted(... etc 
{ 
    Splash.close(); 
    // read data from background thread 
    this.show(); // and other stuff 
} 

は今、あなたはあなたのデータが配信される前にメインウィンドウを起動するためにスプラッシュ画面を閉じていないしないことが保証しています。

その他の考慮事項:バックグラウンドスレッドでアクセスする可能性のあるデータを保護するには、ロックを使用する必要があります。ロックしないで1つ以上のスレッドでデータにアクセスすべきではありません。

+0

実際には、コードをより詳細に読んだら、バックグラウンドスレッドを実行し続けるように思えます。その場合は実行し続けますが、.BeginInvoke()を使用してUIスレッドを呼び出します。 UIスレッドは何もする必要はありません(通常のようなイベントを待つだけです)。 – George

+0

実際に競争条件があります。しかし、backgroundorkerは1つのスレッド用です。私は複数のスレッドを生成し、それらを管理するためにThreadpool.QueueWorkItemを使用しています。BGW経由でスレッドを作成してから、複数のスレッドを起動することはできますか?これは私がスレッディングに取り組んでいて、まったく無知だと感じるのは初めてのことです。また、ThreadPoolにプールできるスレッド数の上限はありますか? – Arcturus

+0

BGWは独自のスレッドを作成します(またはスレッドプールを使用します)。その中からより多くのスレッドを生成することができますが、ReceiverThreadを無期限に実行する必要があるように見えるので、通常のスレッドを使用するだけで意味があります。私のポイントは、あなたのスプラッシュ画面がwhileループでブロックしようとすべきではないということでした。 UIが応答しなくなります。むしろそれを持っているだけです.Show()、そしてイベントを待ちます。その後、(他のスレッドからの).BeginInvoke()は、システムイベントのように配信されます。 – George

関連する問題