新しいスレッドで簡単なコードを実行するにはどうすればよいですか?


340

コードの実行中にフォームがフリーズする(10秒程度)ため、GUIとは異なるスレッドで実行する必要があるコードが少しあります。

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


2
ここでの答えのほとんどは当時は良かったですが、.NET Framework 4.0の改善により物事がより簡単になりました。:この回答で説明するように、Task.Run()メソッドを使用することができますstackoverflow.com/a/31778592/1633949
リチャードIIを

回答:


336

読み始めるのに適した場所は、ジョーアルバハリです。

独自のスレッドを作成する場合、これは簡単です。

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

@EdPower、これはWinformsにのみ適用されますか、それともWebフォームで機能しますか?
MethodMan 2016年

@MethodMan-はい、Webフォームで機能します。ここから開始:
Ed Power

9
IsBackgroundtrueに設定する場合は注意してください。それはおそらくあなたが思っていることをしていません。これは、すべてのフォアグラウンドスレッドが終了したときにスレッドを強制終了するか、またはスレッドがアプリケーションを存続させるかを構成します。実行中にスレッドを終了させたくない場合はIsBackground、trueに設定しないでください。
Zero3

10
@EdPowerどちらにせよ注意する必要があります!おそらく実行の途中で終了したくないタスクの1つの例は、データをディスクに保存するタスクです。ただし、タスクがいつでも終了に適している場合は、フラグは問題ありません。私の要点は、フラグの使用については注意を払う必要があるということです。その目的を説明しなかったため、フラグの命名によって、実際とは異なる何かをしていると簡単に思わせる可能性があります。
Zero3

3
:この回答で説明したように、.NET Frameworkを使用して4.0+ただ、Task.Run()を使用しstackoverflow.com/a/31778592/1633949
リチャードII

193

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#の匿名メソッドを使用してすべてを1つのメソッドに入れていますが、いつでも別のメソッドに引き出すことができます。
  • ProgressChangedまたは RunWorkerCompletedハンドラ内でGUIを更新しても安全 です。ただし、からGUIを更新すると、DoWork が発生し InvalidOperationExceptionます。

27
System.ComponentModelを使用します。(この有用なコード例のおかげで、グーグル検索を行う
手間を省くことができるでしょう

@Gantサンプルコードをありがとうございます。コードを試したところ、ProgressChangedイベントが発生していませんでした。しかし、私がここで同様の受け入れられた答えを試したとき、それはうまくいきました。
マーティン

1
@sooprise Ctrl +。これらの状況であなたを助けます!(Visual Studioを使用していると仮定)
SepehrM '13年

100

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

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

また、私のお気に入り。ジャンクションの 1つのクイックライン。スピードダイヤル(スニペット)にあります。コントロールと非常にうまく機能します。InvokeInvokeRequiredの使い方を知っていれば十分です。
Bitterblue 14

1
+1デリゲートを使用すると、パラメータを適切にラップすることもできます。
ビックフォード

ThreadPool.QueueUserWorkItemとコールバック関数を使用する方法は、ジョブが完了するとコールバックが通知されることを意味します。
Mou

76

速くて汚いですが、うまくいきます:

上部で使用:

using System.Threading;

簡単なコード:

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

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

私はこれを例の新しいコンソールアプリケーションに投げ込みました


8
IsBackgroundのフラグが付いたスレッドは、ランタイムによって自動的に終了されないことに注意してください。これには、アプリによるスレッド管理が必要です。スレッドをバックグラウンドではないとマークしたままにすると、スレッドの実行後にスレッドが終了します。
CmdrTallen 2009年

14
@CmdrTallen:そうではありません。IsBackground = trueとマークされたスレッドは、スレッドがプロセスの終了を停止しないことを意味します。つまり、IsBackground = falseのすべてのスレッドが終了するとプロセスは終了します
Phil Devaney

66

ここに別のオプションがあります:

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

1
この方法でスレッドを作成する場合、UIスレッドからこのスレッドをどのように停止しますか?
slayernoah 2016年

1
@slayernoahでは、CancellationTokenSourceをパラメーターとして渡し、スレッド外でキャンセルトークンを設定できます:msdn.microsoft.com/en-us/library/dd997364(
Spongebob Comrade

7
このコードは、新しいスレッドの作成を保証するものではありません。決定は、使用しているThreadPoolの現在の実装次第です。例として参照してくださいstackoverflow.com/questions/13570579/...
ジュリオCaccin

3
@GiulioCaccin ThreadPoolがプールから既存のスレッドを選択することは可能ですが、それは明らかに別のスレッドです。
スポンジボブ同志2017

40

BackgroundWorkerクラスを使用してみてください。何を実行するか、作業が終了したときに通知を受けるようにデリゲートを与えます。リンク先のMSDNページに例があります。


6
確かにThreadクラスを使用してこれを行うことができますが、BackgroundWorkerはスレッドの完了と進行状況のレポートのためのメソッドを提供します。UIと対話するにはInvokeを使用する必要があることを忘れないでください。
ロバートロスニー2008

1
注意してください:BackgroundWorkerにはいくつかの微妙な制限がありますが、一般的な「離れて何かをしたいのですが、フォームの応答性を維持したい」というのは素晴らしいことです。
Merus、

10
@Merus、これらの微妙な制限について詳しく説明していただけますか?
クリスチャンヒュードン2012

14

値を取得したい場合:

var someValue;

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

thread.Start();

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

6

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

Thread myThread= new Thread(nameOfFunction);

workerThread.Start();

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


3
@ user50612何言ってるの?新しいスレッドはnameOfFunction、現在のGUIスレッドではなく、バックグラウンドで実行されます。IsBackgroundプロパティは、スレッドが生きているのかいないアプリケーションを維持するかどうかを決定します。msdn.microsoft.com/en-us/library/...
ZERO3

5

ここでは、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();
    }
}

3
// 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(上記)のコードは正しくありません。スピンして完了するまで待つ必要はありません。EndInvokeは、WaitHandleが通知されるまでブロックします。

完了するまでブロックしたい場合は、単に

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をリークすることになります。


2

生の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クラスをお勧めします。


IsBackgroundをtrueに設定する場合は注意してください。それはおそらくあなたが思っていることをしていません。これは、すべてのフォアグラウンドスレッドが終了したときにスレッドを強制終了するか、またはスレッドがアプリケーションを存続させるかを構成します。実行中にスレッドを終了させたくない場合は、IsBackgroundをtrueに設定しないでください。
Zero3


1

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

'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
チャールズ、あなたのコードは機能的に異なりint usageCnt = nrgDel.Invoke(lastRunTime, procDT, null, null);ますか?とにかく、現在のスレッドをスリープ状態で一時停止しているようです... GUIスレッドで呼び出すと、GUIがフリーズしても何も役に立たないと思いました
IgorK

はい、BeginInvokeはスレッドプールから別のスレッドのデリゲートを呼び出します... Invokeを使用すると、デリゲートは現在のスレッドで同期的に呼び出されます...しかし、そうです、スリープは間違っています...コールバック関数を使用して結果を収集します。それを示すために編集します
Charles Bretana 2008

1

.Netで個別のスレッドを実行するには多くの方法があり、それぞれに異なる動作があります。GUIの終了後もスレッドを実行し続ける必要がありますか?スレッドとGUIの間で情報を受け渡す必要がありますか?スレッドはGUIを更新する必要がありますか?スレッドは1つのタスクを実行して終了する必要がありますか、それとも実行を継続する必要がありますか?これらの質問に対する答えは、使用する方法を教えてくれます。

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

この記事は、非同期/待機パターンとタスク並列ライブラリが.NETに導入される前に作成されたことに注意してください。


これは私が探していたタイプの情報ですが、あなたの答えはリンク腐敗の犠牲者です。
Chris

教えてくれてありがとう、@ Chris; リンクが修正されました。
Dour High Arch、

0

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

他のスレッドからGUI固有のものへのアクセスには非常に注意する必要があります(多くのGUIツールキットで一般的です)。処理スレッドからGUIで何かを更新したい場合は、WinFormsに役立つと思うこの回答を確認してください。WPFについては、これを参照してください(他のスレッドから機能するようにUpdateProgress()メソッドでコンポーネントにタッチする方法を示していますが、実際には、Dispathcer CheckAccess()BeginInvoke介して実行する前にそれが行われていないので、CheckAccessを参照して検索してください

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

最初の20ページで新しいスレッドとして実行を開始するために必要なものが見つかり、さらに多くのスレッドが含まれていると思います(スレッド化に厳密に固有のGUI固有のスニペットについてはわかりません)。この作品を読んでいるため、コミュニティがこの作品についてどう思っているかを聞いていただければ幸いです。今のところ、(スレッド化のための.NET固有のメソッドとタイプを示すために)私にはかなり見栄えがしました また、私が本当に感謝する.NET 2.0(および古代1.1ではない)についても説明します。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.