2017-11-01 17 views
2

ファイルコピースレッドが実行されている間、uiスレッドを取得してuiを更新できません。私の最終目標は、大きなファイルのコピーが最終的に完了して、プログラムがフリーズしていないことをユーザーに知らせるまでアニメーションを回転させ続けることです。これは、非常にシンプルなサーバーのファイルコピープログラムです。l versC#マルチスレッドの問題

私が間違っていることを誰かに教えてもらえますか?

enter image description here Simple Windows Form

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 
using System.Threading; 

using System.IO; 
using System.Threading.Tasks; 

namespace WindowsFormsApplication1 
{ 
    public partial class Form1 : Form 
    { 
     public Form1() 
     { 
      InitializeComponent(); 
     } 

     private void ResetProgress() 
     { 
      lblStep1.Image = null; 
     } 

     private void SetupProgress() 
     { 
      lblStep1.Image = global::animation1.Properties.Resources.animation; 
     } 

     private void fileCopy() 
     { 
      File.Copy("large file source", "large file destination", true); 
     } 

     private void Form1_Load(object sender, EventArgs e) 
     { 
      lblStep1.Image = global::animation1.Properties.Resources.animation; 
     } 

     private async void button1_Click(object sender, EventArgs e) 
     { 
      SetupProgress(); 
      await Task.Run(() => fileCopy()); 
      ResetProgress(); 
     } 

     private void btnStop_Click(object sender, EventArgs e) 
     { 
      // unhandled currently 
     } 
    } 
} 

*オリジナルバージョン*

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 
using System.Threading; 

using System.IO; 
using System.Threading.Tasks; 

namespace WindowsFormsApplication1 
{ 
    public partial class Form1 : Form 
    { 
     private Thread workItemsProducerThread; 
     private Thread workItemsCopyThread; 
     public Form1() 
     { 
      InitializeComponent(); 
     } 

     private void ResetProgress() 
     { 
      lblStep1.Image = null; 
     } 

     private void SetupProgress() 
     { 
     this.BeginInvoke((MethodInvoker)delegate() 
     { 
      lblStep1.Image = global::animation1.Properties.Resources.animation; 
     }); 
    } 

     private void fileCopy() 
     {   
      File.Copy("Large file source", "Large file destination", true); 

     this.BeginInvoke((MethodInvoker)delegate() 
     { 
      MessageBox.Show("Done"); 
     }); 

     } 

     private void Form1_Load(object sender, EventArgs e) 
     { 
      lblStep1.Image = global::animation1.Properties.Resources.animation; 
     } 

     private void btnStart_Click(object sender, EventArgs e) 
     { 
     this.workItemsProducerThread = new Thread(new ThreadStart(this.SetupProgress)); 
     this.workItemsProducerThread.IsBackground = true; 
     this.workItemsProducerThread.Start(); 

     this.SetupProgress(); 

      this.workItemsCopyThread = new Thread(new ThreadStart(this.fileCopy)); 
      this.workItemsCopyThread.IsBackground = true; 
      this.workItemsCopyThread.Start(); 


     while (workItemsCopyThread.IsAlive) 
     { 
      Thread.Sleep(1000); // wait 
     } 

     MessageBox.Show("Done"); 
    } 

     private void btnStop_Click(object sender, EventArgs e) 
     { 
      if (this.workItemsProducerThread != null) 
      { 
       this.workItemsProducerThread.Abort(); 
       lblStep1.Image = global::animation1.Properties.Resources.animation; 
      } 
     } 

     private void btnTest_Click(object sender, EventArgs e) 
     { 
      fileCopy(); 
     } 
    } 

}

+0

このoldyスタイルを試してみてください。それは悪い考えです。 –

+0

私はこれらの提案を試しましたが、症状は同じです。私は、メインUIからSetupProgressを実行し、begininvokeを呼び出しただけで置き換えようとしましたが、動作は変更されません。 – Jay

+0

@Jayその理由は、コードが複雑すぎるためです。あなたはスレッド、呼び出し、または中止は必要ありません。 Henk Holtermanの答えは、組み込みのTask.Runとasync/awaitを使ってこれを書くのが簡単であることを示しています。 –

答えて

2

あなたのクリックハンドラで寝ないでください。これはUIスレッドをフリーズさせます。クロックハンドラを終了させて​​ください。あなたのファイルコピースレッドでは、コピーはしません。 Invoke(またはBeginInvoke)を使用して、完了したメッセージボックスをUIスレッドにポップアップさせます。

+0

私が正しく理解していれば、私は上記の訂正を行いましたが、まだ同じ症状を経験していますか? – Jay

+0

Task.Runと 'async/await'が利用可能な場合にInvokeまたはスレッドを使用する理由はありません –

+0

whileハンドラループとclickハンドラからのメッセージボックスを削除する必要があります。 (非同期は簡単ですが) –

1

それは非同期でそんなに簡単になりました/待つ:

DoSomeWork()はわずかの距離UIからとスレッドからとどまるべき
private async void button1_Click(object sender, EventArgs e) 
{ 
    SetupProgress(); 
    await Task.Run(() => DoSomeWork()); 
    Resetprogress(); 
} 


他のすべてはメインスレッドで実行され、呼び出しは必要ありません。

+0

私の現在の編集内容は、あなたがアドバイスしている内容を反映していますか?私は上記のコードをあなたの提案に基づいて試してみましたが、それでも同じ症状が現れるので、私はあなたが誤解していると思います。 – Jay

+0

@Jay *このコードはフリーズしません。 *あなたの*コードにはまだUIを更新しようとする不要な 'Invoke'があります。それをしないでください。メッセージボックスを表示する場合は、 'await'の後にそれを行います。 'await'は' Invoke'と 'BeginInvoke'を冗長にします。 –

+0

メッセージボックスを削除しました。ファイルがまだコピーされているので、コードがフリーズしているとは思わないが、begininvoke/messageboxを削除してもUIはアニメーションGIFを表示しない? – Jay

0

は逆に、別のスレッドでSetupProgressを実行する理由はありません

private void SetupProgress() 
     { 

      Invoke((MethodInvoker) delegate 
      { 
       lblStep1.Image = global::animation1.Properties.Resources.animation; 
      }); 

     } 


private Thread TDoSomeWork() 
     { 
      var t = new Thread(() => DoSomeWork()); 
      t.Start(); 
      return t; 
     } 


TDoSomeWork(); 
+0

あなたはそれを2度起動します。しかし、なぜスレッドを作成するのですか? ThreadPool&Tasksの何が問題なのですか? –

関連する問題