web-dev-qa-db-ja.com

新しいスレッドで簡単なコードを実行する方法

私はGUIとは別のスレッドで実行する必要があるコードを少し持っています。コードが実行されている間フォームがフリーズしてしまうからです(10秒程度)。

以前に新しいスレッドを作成したことがないとします。 C#で.NET Framework 2.0以降を使用してこれを行う方法の簡単な/基本的な例は何ですか?

314
Tim M

読み始めるのに良い場所は Joe Albahari です。

あなたがあなた自身のスレッドを作成したい場合、これはそれが得るのと同じくらい簡単です:

using System.Threading;
new Thread(() => 
{
    Thread.CurrentThread.IsBackground = true; 
    /* run your code here */ 
    Console.WriteLine("Hello, world"); 
}).Start();
306
Ed Power

BackgroundWorkerはあなたにとって最良の選択のようです。

これが私の最小の例です。ボタンをクリックすると、バックグラウンドワーカーはバックグラウンドスレッドで作業を開始し、同時に進行状況を報告します。作業完了後も報告されます。

using System.ComponentModel;
...
    private void button1_Click(object sender, EventArgs e)
    {
        BackgroundWorker bw = new BackgroundWorker();

        // this allows our worker to report progress during work
        bw.WorkerReportsProgress = true;

        // what to do in the background thread
        bw.DoWork += new DoWorkEventHandler(
        delegate(object o, DoWorkEventArgs args)
        {
            BackgroundWorker b = o as BackgroundWorker;

            // do some simple processing for 10 seconds
            for (int i = 1; i <= 10; i++)
            {
                // report the progress in percent
                b.ReportProgress(i * 10);
                Thread.Sleep(1000);
            }

        });

        // what to do when progress changed (update the progress bar for example)
        bw.ProgressChanged += new ProgressChangedEventHandler(
        delegate(object o, ProgressChangedEventArgs args)
        {
            label1.Text = string.Format("{0}% Completed", args.ProgressPercentage);
        });

        // what to do when worker completes its task (notify the user)
        bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
        delegate(object o, RunWorkerCompletedEventArgs args)
        {
            label1.Text = "Finished!";
        });

        bw.RunWorkerAsync();
    }

注:

  • わかりやすくするために、C#の無名メソッドを使用してすべてを単一のメソッドにまとめましたが、いつでもさまざまなメソッドに引き出すことができます。
  • ProgressChangedまたはRunWorkerCompletedハンドラー内でGUIを更新しても安全です。しかし、GUIをDoWorkから更新するとInvalidOperationExceptionが発生します。
190
Gant

ThreadPool.QueueUserWorkItem は単純なものには非常に理想的です。唯一の注意点は、他のスレッドからコントロールにアクセスすることです。

System.Threading.ThreadPool.QueueUserWorkItem(delegate {
    DoSomethingThatDoesntInvolveAControl();
}, null);
96
Mark Brackett

早くて汚れていますが、うまくいきます。

上で使う:

using System.Threading;

簡単なコード:

static void Main( string[] args )
{
    Thread t = new Thread( NewThread );
    t.Start();
}

static void NewThread()
{
    //code goes here
}

例として、これを新しいコンソールアプリケーションに投入しただけです。

72
FallenAvatar

これは別の選択肢です:

Task.Run(()=>{
//Here is a new thread
});
61

BackgroundWorker クラスを使用してみてください。何を実行し、作業が終了したときに通知を受けるかを委任します。私がリンクしたMSDNページに例があります。

40
Andy

値を取得したい場合は、

var someValue;

Thread thread = new Thread(delegate()
            {                 
                //Do somthing and set your value
                someValue = "Hello World";
            });

thread.Start();

while (thread.IsAlive)
  Application.DoEvents();
14
Starkey

そのコードを関数(GUIと同じスレッドでは実行できないコード)に入れ、そのコードの実行をトリガーするには次のようにします。

Thread myThread= new Thread(nameOfFunction);

workerThread.Start();

スレッドオブジェクトでstart関数を呼び出すと、新しいスレッドで関数呼び出しが実行されます。

6
Redbaron

ここでは、スレッドがどのように動作するかを理解するためだけに、progressBarでスレッドを使用できます。3つのprogressBarと4つのボタンがあります。

 public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    Thread t, t2, t3;
    private void Form1_Load(object sender, EventArgs e)
    {

        CheckForIllegalCrossThreadCalls = false;

         t = new Thread(birinicBar); //evry thread workes with a new progressBar


         t2 = new Thread(ikinciBar);


         t3 = new Thread(ucuncuBar);

    }

    public void birinicBar() //to make progressBar work
    {
        for (int i = 0; i < 100; i++) {
            progressBar1.Value++;
            Thread.Sleep(100); // this progressBar gonna work faster
        }
    }

    public void ikinciBar()
    {
        for (int i = 0; i < 100; i++)
        {
            progressBar2.Value++;
            Thread.Sleep(200);
        }


    }

    public void ucuncuBar()
    {
        for (int i = 0; i < 100; i++)
        {
            progressBar3.Value++;
            Thread.Sleep(300);
        }
    }

    private void button1_Click(object sender, EventArgs e) //that button to start the threads
    {
        t.Start();
        t2.Start(); t3.Start();

    }

    private void button4_Click(object sender, EventArgs e)//that button to stup the threads with the progressBar
    {
        t.Suspend();
        t2.Suspend();
        t3.Suspend();
    }

    private void button2_Click(object sender, EventArgs e)// that is for contuniue after stuping
    {
        t.Resume();
        t2.Resume();
        t3.Resume();
    }

    private void button3_Click(object sender, EventArgs e) // finally with that button you can remove all of the threads
    {
        t.Abort();
        t2.Abort();
        t3.Abort();
    }
}
4
Ammar Alyasry
// following declaration of delegate ,,,
public delegate long GetEnergyUsageDelegate(DateTime lastRunTime, 
                                            DateTime procDateTime);

// following inside of some client method
GetEnergyUsageDelegate nrgDel = GetEnergyUsage;
IAsyncResult aR = nrgDel.BeginInvoke(lastRunTime, procDT, null, null);
while (!aR.IsCompleted) Thread.Sleep(500);
int usageCnt = nrgDel.EndInvoke(aR);

Charlesのコード(上のコード)は正しくありません。完了までスピン待機する必要はありません。 WaitHandleが通知されるまでEndInvokeはブロックします。

あなたが完了するまでブロックしたい場合は、単にする必要があります

nrgDel.EndInvoke(nrgDel.BeginInvoke(lastRuntime,procDT,null,null));

または代わりに

ar.AsyncWaitHandle.WaitOne();

しかし、ブロックした場合にanyc呼び出しを発行するポイントは何ですか?同期呼び出しを使うこともできます。もっと良い方法は、クリーンアップのためにラムダをブロックして渡さないことです。

nrgDel.BeginInvoke(lastRuntime,procDT,(ar)=> {ar.EndInvoke(ar);},null);

覚えておくべき1つのことは、あなたがEndInvokeを呼び出さなければならないということです。多くの人がこれを忘れて、ほとんどの非同期実装がEndInvokeでwaithandleを解放するので、WaitHandleをリークすることになります。

3
Matt Davison

生のThreadオブジェクトを使用する場合は、少なくともIsBackgroundをtrueに設定する必要があります。また、Threading Apartmentモデル(おそらくSTA)も設定する必要があります。

public static void DoWork()
{
    // do some work
}

public static void StartWorker()
{
    Thread worker = new Thread(DoWork);
    worker.IsBackground = true;
    worker.SetApartmentState(System.Threading.ApartmentState.STA);
    worker.Start()
}

UIインタラクションが必要な場合は、BackgroundWorkerクラスをお勧めします。

2
user50612

.NETで別々のスレッドを実行する方法はたくさんありますが、それぞれが異なる動作をします。 GUIが終了した後もスレッドを実行し続ける必要がありますか?スレッドとGUIの間で情報をやり取りする必要がありますか?スレッドはGUIを更新する必要がありますか?スレッドは1つのタスクを実行してから終了するべきですか、それとも実行し続けるべきですか?これらの質問に対する答えは、どの方法を使うべきかあなたに教えます。

Code ProjectのWebサイトには、さまざまなメソッドについて説明し、サンプルコードを提供する優れた 非同期メソッドの記事 があります。

この記事は、 async/await パターンと Task Parallel Library が.NETに導入される前に書かれたものです。

1
Dour High Arch

デリゲートとスレッドプールを使用する別のオプション...

'GetEnergyUsage'がDateTimeと別のDateTimeを入力引数とし、Intを返すメソッドであると仮定します。

// following declaration of delegate ,,,
public delegate long GetEnergyUsageDelegate(DateTime lastRunTime, 
                                            DateTime procDateTime);

// following inside of some client method 
GetEnergyUsageDelegate nrgDel = GetEnergyUsage;                     
IAsyncResult aR = nrgDel.BeginInvoke(lastRunTime, procDT, null, null);
while (!aR.IsCompleted) Thread.Sleep(500);
int usageCnt = nrgDel.EndInvoke(aR);
1
Charles Bretana

方法:ファイルを検索するためにバックグラウンドスレッドを使用する

他のスレッドからGUI固有のものへのアクセスには細心の注意を払う必要があります(多くのGUIツールキットでは一般的です)。処理スレッドチェック からGUIで何かを更新したいのであれば、この答え はWinFormsに役立つと思います。 WPFの場合は this を参照してください(これはUpdateProgress()メソッドのコンポーネントに触れる方法を示しているので、他のスレッドからも動作しますが、実際は好きではありません) Dispathcerを介してBeginInvokeを実行する前にCheckAccess()を実行していません。を参照し、その中でCheckAccessを検索してください)

スレッドに関する.NET特有の本を探していて、これを見つけた(無料でダウンロード可能)。詳細については、 http://www.albahari.com/threading/ を参照してください。

私はあなたがあなたが最初の20ページで新しいスレッドとして実行を開始するために必要なものを見つけるであろうと信じています、そしてそれはもっとたくさんあります(私が厳密にスレッドに特定することを意味します私がこれを読んでいるので、コミュニティがこの作品についてどう思うか聞いて喜んでいるでしょう。今のところ私にとってはかなりきれいに見えました(スレッド化のための.NET特有のメソッドとタイプを示すために)。また、それは私が本当に感謝しているものを含みます。

0
IgorK

私はJeff Richterの Power Threading Library と、特にIAsyncEnumeratorを見ることをお勧めします。 Charlie Calvertの ブログで、Richterがその概要を説明しているビデオを見てください。

非同期プログラミングタスクをコーディングしやすくするので、その名前に納得しないでください。

0
Robert Paulson