Yeni bir iş parçacığında basit bir kod parçasını nasıl çalıştırabilirim?


340

Şu anda kod çalışırken (10 saniye ya da öylesine) form dondurmak neden olduğu gibi GUI farklı bir iş parçacığında çalıştırmanız gereken kod biraz var.

Daha önce hiç yeni bir konu oluşturmadığımı varsayın; C # ve .NET Framework 2.0 veya sonraki sürümlerini kullanarak bunu nasıl basit / temel bir örnek nedir?


2
Buradaki cevapların çoğu o zamanlar iyiydi, ancak .NET Framework 4.0'daki geliştirmeler işleri kolaylaştırıyor. Bu yanıtta açıklandığı gibi Task.Run () yöntemini kullanabilirsiniz: stackoverflow.com/a/31778592/1633949
Richard II

Yanıtlar:


336

Okumaya başlamak için iyi bir yer Joe Albahari .

Kendi iş parçacığınızı oluşturmak istiyorsanız, bu kadar basit:

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

@EdPower, bu sadece Winforms için mi geçerli? Yoksa Web Formlarında mı çalışacak ..?
MethodMan

@MethodMan - Evet, Web Formlarında çalışır. Buradan başlayın:
Ed Power

9
IsBackgroundDoğru değerine ayarlama konusunda dikkatli olun . Muhtemelen düşündüğünü yapmaz. Yaptığı şey, tüm ön plan iş parçacıkları öldüğünde iş parçacığının öldürülüp öldürülmeyeceğini veya iş parçacığının uygulamayı canlı tutup tutmayacağını yapılandırmaktır. İş parçacığınızın yürütmenin ortasında bitmesini istemiyorsanız IsBackground, true olarak ayarlamayın .
Sıfır3

10
@EdPower Bence her iki şekilde de dikkatli olmalısın! Yürütme işlemini muhtemelen sonlandırmak istemediğiniz bir göreve örnek, verileri diske kaydeden bir görevdir. Ancak, göreviniz herhangi bir zamanda fesih için uygunsa, bayrak iyidir. Demek istediğim , bayrağını kullanma konusunda dikkatli olmanız gerektiğiydi , çünkü amacını tarif etmediniz ve isimlendirmesi, gerçekte olduğundan daha başka bir şey yaptığını düşünmesine neden olabilir.
Sıfır3

3
.NET Framework 4.0+ ile bu yanıtta açıklandığı gibi Task.Run () yöntemini kullanın: stackoverflow.com/a/31778592/1633949
Richard II

193

BackgroundWorker sizin için en iyi seçim gibi görünüyor.

İşte benim minimal örneğim. Düğmeye tıkladıktan sonra, arka plan çalışanı arka plan iş parçacığında çalışmaya başlayacak ve ilerlemesini aynı anda rapor edecektir. Ayrıca çalışma tamamlandıktan sonra da rapor verecektir.

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();
    }

Not:

  • Ben basitlik için C # 'ın anonim yöntemini kullanarak her şeyi tek bir yöntem koymak ama her zaman farklı yöntemlere onları çekebilirsiniz.
  • İçindeki ProgressChangedveya RunWorkerCompletedişleyicilerin GUI'sini güncellemek güvenlidir . Ancak, GUI'nin güncellenmesi DoWork buna neden olur InvalidOperationException.

27
System.ComponentModel Kullanımı; (insanların bir google araması yapmasını engelleyebilir, bu yararlı kod örneği için teşekkürler) +1
sooprise

@Gant Örnek kod için teşekkür ederiz. Kodunuzu denediğimde ProgressChanged olayı tetiklenmedi. Ancak burada benzer kabul edilen cevabı denediğimde [ stackoverflow.com/questions/23112676/… çalıştı.
Martin

1
@sooprise Ctrl +. bu durumlarda size yardımcı olur! (Visual Studio kullandığınız varsayılarak)
SepehrM

100

ThreadPool.QueueUserWorkItem şey basit için oldukça idealdir. Tek uyarı diğer iplikten bir kontrole erişmektir.

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

Ayrıca benim favorim. Kavşak için hızlı bir hat . Hızlı aramada (snippet) var. Kontroller ile çok iyi çalışır. Sadece Invoke ve InvokeRequired'i nasıl kullanacağınızı bilmeniz gerekir .
Bitterblue

1
+1 Temsilcinin parametreleri düzgün bir şekilde sarmanıza izin verdiğini unutmayın.
Bickford

ThreadPool.QueueUserWorkItem nasıl geri arama fonksiyonu ile kullanılır iş bittiğinde geri çağırmak bize bildirir anlamına gelir.
Mou

76

Hızlı ve kirli, ama işe yarayacak:

Üstte kullanma:

using System.Threading;

basit kod:

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

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

Bunu yeni bir konsol uygulaması için yeni bir konsol uygulamasına attım


8
IsBackground olarak işaretlenen iş parçacıklarının Çalışma Zamanı tarafından otomatik olarak sonlandırılmadığını unutmayın. Bu, uygulama tarafından iş parçacığı yönetimi gerektirir. Eğer iş parçacığı arka plan olarak işaretlenmezse, iş parçacığı yürütüldükten sonra iş parçacığı sizin için sonlandırılır.
CmdrTallen

14
@CmdrTallen: Bu pek doğru değil. IsBackground = true olarak işaretlenmiş bir iş parçacığı, iş parçacığının işlemin çıkmasını durdurmayacağı anlamına gelir; yani IsBackground = false olan tüm iş parçacıkları çıktıktan sonra bir işlem çıkacaktır
Phil Devaney

66

İşte başka bir seçenek:

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

1
bu şekilde bir iş parçacığı oluşturursanız, bu iş parçacığını UI iş parçacığından nasıl durdurabilirsiniz?
slayernoah

1
@slayernoah bir CancellationTokenSource parametresi olarak geçebilir ve iptal belirtecini iş parçacığının dışında ayarlayabilirsiniz: msdn.microsoft.com/en-us/library/dd997364(v=vs.110).aspx
Spongebob Comrade

7
Bu kod, yeni bir iş parçacığına sahip olmanızı garanti etmez. Karar, kullandığınız ThreadPool'un geçerli uygulamasına bağlıdır. Örnek olarak bakınız stackoverflow.com/questions/13570579/…
Giulio Caccin

3
@GiulioCaccin ThreadPool'un havuzundan mevcut bir iş parçacığı seçmesi mümkündür, ancak kesinlikle farklı bir iş parçacığıdır.
Sünger Bob Yoldaş

40

BackgroundWorker sınıfını kullanmayı deneyin . Neyin çalıştırılacağı ve iş bittiğinde bilgilendirilmek için delegelere veriyorsunuz. Bağlantı verdiğim MSDN sayfasında bir örnek var.


6
Bunu kesinlikle Thread sınıfıyla yapabilirsiniz, ancak BackgroundWorker, aksi takdirde kendinizi nasıl kullanacağınızı anlamanız gereken iplik tamamlama ve ilerleme raporlaması için yöntemler sunar. Kullanıcı arayüzüyle konuşmak için Invoke'u kullanmanız gerektiğini unutmayın!
Robert Rossney

1
Dikkat edin: BackgroundWorker'ın bazı ince sınırlamaları vardır, ancak ortak "Uzaklara gidip bir şeyler yapmak ve formumun duyarlı kalmasını istiyorum" harika.
Merus

10
@Merus, bu ince sınırlamaların ne olduğunu genişletebilir misiniz?
Christian Hudon

14

Bir değer almak istiyorsanız:

var someValue;

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

thread.Start();

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

6

Bu kodu bir işleve (GUI ile aynı iş parçacığında yürütülemeyen kod) koyun ve bu kodun yürütülmesini tetiklemek için aşağıdakileri koyun.

Thread myThread= new Thread(nameOfFunction);

workerThread.Start();

İş parçacığı nesnesinde start işlevinin çağrılması, işlev çağrınızın yeni bir iş parçacığında yürütülmesine neden olur.


3
@ user50612 Neden bahsediyorsun? Yeni iş parçacığı nameOfFunctiongeçerli GUI iş parçacığında değil arka planda çalışır . IsBackgroundMülkiyet parçacığı canlı ya da değil uygulamayı tutacak belirler: msdn.microsoft.com/en-us/library/...
Zero3

5

Bir progressBar ile iş parçacıklarını nasıl kullanabilirsiniz, sadece iş parçacıklarının nasıl çalıştığının altını çizmek için, formda üç progressBar ve 4 düğmesi vardır:

 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 kodunuz (yukarıda) doğru değil. Tamamlanması için döndürmeyi beklemenize gerek yoktur. EndInvoke, WaitHandle sinyali verilene kadar bloke olur.

Tamamlanıncaya kadar engellemek istiyorsanız,

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

Veya alternatif olarak

ar.AsyncWaitHandle.WaitOne();

Ancak, engellerseniz anyc çağrıları yapmanın anlamı nedir? Senkronize bir çağrı da kullanabilirsiniz. Daha iyi bir bahis, temizlik için bir lambda'yı engellememek ve geçmek değil:

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

Akılda tutulması gereken bir şey, EndInvoke'u aramanız gerektiğidir . Çoğu insan bunu unutur ve çoğu asenkron uygulama EndInvoke'deki bekleme kolunu serbest bıraktığından WaitHandle'ı sızdırır.


2

Raw Thread nesnesini kullanacaksanız, IsBackground öğesini minimum olarak true değerine ayarlamanız gerekir ve ayrıca Diş Açma Dairesi modelini (muhtemelen STA) ayarlamanız gerekir.

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()
}

Kullanıcı arabirimi etkileşimi gerekiyorsa BackgroundWorker sınıfı tavsiye ederim.


IsBackground'ı true değerine ayarlama konusunda dikkatli olun. Muhtemelen düşündüğünü yapmaz. Yaptığı şey, tüm ön plan iş parçacıkları öldüğünde iş parçacığının öldürülüp öldürülmeyeceğini veya iş parçacığının uygulamayı canlı tutup tutmayacağını yapılandırmaktır. İş parçacığınızın yürütmenin ortasında sonlandırılmasını istemiyorsanız, IsBackground öğesini true olarak ayarlamayın.
Sıfır3


1

delegeler ve İş Parçacığı Havuzu kullanan başka bir seçenek ...

'GetEnergyUsage' varsayalım bir argüman olarak DateTime ve başka bir DateTime alır ve bir Int döndürür ...

// 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, kodunuz işlevsel olarak farklı int usageCnt = nrgDel.Invoke(lastRunTime, procDT, null, null);mı? Bu zaten uyku ile şimdiki iplik askıya gibi görünüyor ... GUI konu olarak adlandırırsanız GUI donma hiçbir şey yardımcı olacağını düşündüm
IgorK

Evet, BeginInvoke temsilci iş parçacığı havuzundan başka bir iş parçacığında çağırır ... Invoke kullanırsanız, temsilci geçerli iş parçacığında eşzamanlı olarak çağrılır ... Ama haklısın, uyku yanlış ... bunu ortadan kaldırmalısın ve bir geri çağırma işlevi kullanarak sonuçları toplar. Bunu göstermek için düzenleyeceğim
Charles Bretana

1

.Net'te ayrı iş parçacıkları çalıştırmanın birçok yolu vardır, her birinin farklı davranışları vardır. GUI'den çıktıktan sonra iş parçacığını çalıştırmaya devam etmeniz gerekiyor mu? İş parçacığı ve GUI arasında bilgi aktarmanız mı gerekiyor? İş parçacığının GUI'yi güncellemesi gerekiyor mu? İş parçacığı bir görevden sonra çıkmalı mı yoksa çalışmaya devam etmeli mi? Bu soruların cevapları size hangi yöntemi kullanacağınızı söyleyecektir.

Code Project web sitesinde çeşitli yöntemleri açıklayan ve örnek kod sağlayan iyi bir zaman uyumsuz yöntem makalesi bulunmaktadır.

Bu makalenin, zaman uyumsuz / beklemede kalıp ve Görev Paralel Kitaplığı .NET'e girmeden önce yazıldığını unutmayın .


Bu aradığım bilgi türüdür, ancak cevabınız link rot'un kurbanıdır.
Chris

Bana bildirdiğiniz için teşekkürler, @Chris; bağlantı düzeltildi.
Dour High Arch

0

Nasıl yapılır: Dosyaları Aramak için Arka Plan İş Parçacığını Kullanma

Diğer iş parçacıklarından GUI'ye özgü öğelere erişim konusunda çok dikkatli olmalısınız (birçok GUI araç kiti için yaygındır). GUI bir şey iş parçacığı işlemek güncelleştirmek istiyorsanız, WinForms için yararlı olduğunu düşünüyorum bu yanıtı kontrol edin . WPF için bu bakın (bu, diğer iş parçacıklarından çalışacak şekilde UpdateProgress () yönteminde bileşen dokunmayı gösterir, ama aslında Dispathcer ile yapmadan CheckAccess()önce yapmıyor gibi değil BeginInvoke, bakın ve içinde CheckAccess arama)

Diş üzerinde .NET belirli bir kitabı arayan ve tespit edilmiştir bu bir (ücretsiz indirilebilir). Bununla ilgili daha fazla bilgi için http://www.albahari.com/threading/ adresine bakın .

İlk 20 sayfada yürütmeyi başlatmak için gerekeni bulacağınıza inanıyorum ve çok daha fazlası var (GUI'ye özel snippet'lerden kesinlikle emin değilim. Topluluğun bu çalışma hakkında ne düşündüğünü duymaktan memnuniyet duyarım çünkü bunu okuyorum. Şimdilik benim için oldukça temiz görünüyordu (.NET'e özgü yöntemleri ve diş çekme türlerini göstermek için). Ayrıca gerçekten takdir ediyorum .NET 2.0 (ve eski 1.1 değil) kapsar.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.