Bagaimana cara menjalankan sedikit kode sederhana di utas baru?


340

Saya memiliki sedikit kode yang harus saya jalankan di utas yang berbeda dari GUI karena saat ini menyebabkan formulir membeku sementara kode berjalan (sekitar 10 detik).

Asumsikan saya belum pernah membuat utas baru sebelumnya; apa contoh sederhana / dasar tentang bagaimana melakukan ini di C # dan menggunakan .NET Framework 2.0 atau yang lebih baru?


2
Sebagian besar jawaban di sini baik pada saat itu, tetapi perbaikan dalam .NET Framework 4.0 membuat semuanya lebih sederhana. Anda dapat menggunakan metode Task.Run (), seperti yang dijelaskan dalam jawaban ini: stackoverflow.com/a/31778592/1633949
Richard II

Jawaban:


336

Tempat yang baik untuk mulai membaca adalah Joe Albahari .

Jika Anda ingin membuat utas sendiri, ini sesederhana mungkin:

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

@ PowerPower, apakah ini hanya berlaku untuk Winforms .. atau akankah ini berfungsi dalam Formulir Web ..?
MethodMan

@MethodMan - Ya, itu akan berfungsi di Formulir Web. Mulai di sini:
Ed Power

9
Berhati-hatilah dengan pengaturan IsBackgroundmenjadi true. Mungkin tidak melakukan apa yang Anda pikirkan. Apa yang dilakukannya, adalah mengonfigurasi apakah utas akan dimatikan ketika semua utas depan telah mati, atau apakah utas akan membuat aplikasi tetap hidup. Jika Anda tidak ingin utas Anda dihentikan pada pertengahan eksekusi, jangan setel IsBackgroundke true.
Zero3

10
@ Power, saya pikir Anda harus berhati-hati! Salah satu contoh tugas yang Anda mungkin tidak ingin menghentikan eksekusi tengah adalah salah satu yang menyimpan data ke disk. Tetapi tentu saja, jika tugas Anda cocok untuk penghentian kapan saja, benderanya baik-baik saja. Maksud saya adalah bahwa seseorang perlu berhati-hati dalam menggunakan bendera , karena Anda tidak menggambarkan tujuannya, dan penamaannya dapat dengan mudah membuat orang percaya bahwa ia melakukan sesuatu yang lain daripada apa yang sebenarnya dilakukannya.
Zero3

3
dengan .NET Framework 4.0+ cukup gunakan Task.Run (), seperti dijelaskan dalam jawaban ini: stackoverflow.com/a/31778592/1633949
Richard II

193

BackgroundWorker tampaknya menjadi pilihan terbaik untuk Anda.

Inilah contoh minimal saya. Setelah Anda mengklik tombol pekerja latar akan mulai bekerja di utas latar dan juga melaporkan kemajuannya secara bersamaan. Ini juga akan melaporkan setelah pekerjaan selesai.

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

catatan:

  • Saya meletakkan semuanya dalam satu metode menggunakan metode anonim C # untuk kesederhanaan tetapi Anda selalu dapat menarik mereka ke metode yang berbeda.
  • Aman untuk memperbarui GUI di dalam ProgressChangedatau RunWorkerCompletedpenangan. Namun, memperbarui GUI dari DoWork akan menyebabkan InvalidOperationException.

27
Menggunakan System.ComponentModel; (mungkin menyelamatkan orang dari melakukan pencarian google, terima kasih untuk contoh kode bermanfaat ini) +1
sooprise

@Gant Terima kasih atas kode sampel. Ketika saya mencoba kode Anda, acara ProgressChanged tidak diaktifkan. Namun ketika saya mencoba jawaban yang sama yang diterima di sini [ stackoverflow.com/questions/23112676/... itu berhasil.
Martin

1
@sooprise Ctrl +. membantu Anda dalam situasi ini! (Dengan asumsi Anda menggunakan Visual Studio)
SepehrM

100

The ThreadPool.QueueUserWorkItem cukup ideal untuk sesuatu yang sederhana. Satu-satunya peringatan adalah mengakses kontrol dari utas lainnya.

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

Juga favorit saya. Satu jalur cepat untuk persimpangan . Saya memilikinya di speed dial (snippet). Bekerja sangat baik dengan Kontrol. Anda hanya perlu tahu cara menggunakan Invoke dan InvokeRequired .
Bitterblue

1
+1 Perhatikan bahwa delegasi memungkinkan Anda untuk membungkus parameter dengan rapi juga.
Will Bickford

cara menggunakan ThreadPool.QueueUserWorkItem dengan fungsi callback berarti ketika pekerjaan selesai maka panggilan kembali akan memberi tahu kami.
Mou

76

Cepat dan kotor, tetapi akan berhasil:

Menggunakan di atas:

using System.Threading;

kode sederhana:

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

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

Saya baru saja melemparkan ini ke aplikasi konsol baru untuk exmaple


8
Ingat utas yang ditandai IsBackground tidak diakhiri secara otomatis oleh Runtime. Ini membutuhkan manajemen utas oleh aplikasi. Jika Anda membiarkan utas ditandai sebagai bukan latar maka setelah utas eksekusi utas diakhiri untuk Anda.
CmdrTallen

14
@ CmdrTallen: Itu tidak benar. Utas bertanda IsBackground = true berarti utas tidak akan menghentikan proses keluar yaitu proses akan keluar ketika semua utas dengan IsBackground = false telah keluar
Phil Devaney

66

Berikut ini pilihan lain:

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

1
jika Anda membuat utas dengan cara ini, bagaimana Anda menghentikan utas ini dari utas UI?
slayernoah

1
@slayernoah Anda dapat memberikan PembatalanTokenSource sebagai parameter untuk itu dan mengatur token pembatalan di luar utas: msdn.microsoft.com/en-us/library/dd997364(v=vs.110).aspx
Spongebob Kamerad

7
Kode ini tidak akan menjamin Anda memiliki utas baru. Keputusannya bergantung pada implementasi ThreadPool yang Anda gunakan saat ini. Lihat sebagai contoh stackoverflow.com/questions/13570579/…
Giulio Caccin

3
@GiulioCaccin Adalah mungkin bagi ThreadPool untuk memilih utas yang sudah ada dari kumpulannya tetapi ini jelas merupakan utas yang berbeda.
Spongebob Kamerad

40

Coba gunakan kelas BackgroundWorker . Anda memberinya delegasi untuk apa yang harus dijalankan, dan untuk diberitahu ketika pekerjaan telah selesai. Ada contoh di halaman MSDN yang saya tautkan.


6
Anda tentu bisa melakukan ini dengan kelas Utas, tetapi BackgroundWorker memberi Anda metode untuk penyelesaian utas dan pelaporan kemajuan yang seharusnya Anda harus memikirkan cara menggunakan sendiri. Jangan lupa bahwa Anda harus menggunakan Invoke untuk berbicara dengan UI!
Robert Rossney

1
Berhati-hatilah: BackgroundWorker memiliki beberapa batasan yang halus, tetapi untuk umum "Saya ingin pergi dan melakukan sesuatu dan membuat formulir saya tetap responsif" itu fantastis.
Merus

10
@Merus, dapatkah Anda memperluas batasan-batasan tersamar itu?
Christian Hudon

14

Jika Anda ingin mendapatkan nilai:

var someValue;

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

thread.Start();

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

6

Letakkan kode itu dalam suatu fungsi (kode yang tidak bisa dieksekusi di utas yang sama dengan GUI), dan untuk memicu eksekusi kode itu, letakkan yang berikut ini.

Thread myThread= new Thread(nameOfFunction);

workerThread.Start();

Memanggil fungsi mulai pada objek utas akan menyebabkan pelaksanaan panggilan fungsi Anda di utas baru.


3
@ user50612 Apa yang kamu bicarakan? Utas baru akan berjalan nameOfFunctiondi latar belakang, dan bukan pada utas GUI saat ini. The IsBackgroundproperti menentukan apakah benang akan tetap aplikasi hidup atau tidak: msdn.microsoft.com/en-us/library/...
Zero3

5

Di sini bagaimana caranya menggunakan utas dengan progressBar, itu hanya untuk menggarisbawahi cara kerja utas, dalam bentuk ada tiga tombol progressBar dan 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 kode Anda (di atas) tidak benar. Anda tidak perlu berputar menunggu sampai selesai. EndInvoke akan memblokir hingga WaitHandle diberi sinyal.

Jika Anda ingin memblokir hingga selesai, Anda hanya perlu melakukannya

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

atau sebagai alternatif

ar.AsyncWaitHandle.WaitOne();

Tapi apa gunanya mengeluarkan panggilan telepon jika Anda memblokir? Anda mungkin juga hanya menggunakan panggilan sinkron. Taruhan yang lebih baik adalah dengan tidak memblokir dan meneruskan lambda untuk pembersihan:

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

Satu hal yang perlu diingat adalah Anda harus menelepon EndInvoke. Banyak orang lupa ini dan akhirnya membocorkan WaitHandle karena kebanyakan implementasi async melepaskan waithandle di EndInvoke.


2

Jika Anda akan menggunakan objek Thread mentah maka Anda harus mengatur IsBackground menjadi true minimum dan Anda juga harus mengatur model Threading Apartment (mungkin 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()
}

Saya akan merekomendasikan kelas BackgroundWorker jika Anda memerlukan interaksi UI.


Berhati-hatilah dalam mengatur IsBackground menjadi true. Mungkin tidak melakukan apa yang Anda pikirkan. Apa yang dilakukannya, adalah mengonfigurasi apakah utas akan dimatikan ketika semua utas depan telah mati, atau apakah utas akan membuat aplikasi tetap hidup. Jika Anda tidak ingin utas Anda dihentikan saat eksekusi, jangan setel IsBackground menjadi true.
Zero3


1

opsi lain, yang menggunakan delegasi dan Thread Pool ...

dengan asumsi 'GetEnergyUsage' adalah metode yang mengambil DateTime dan DateTime lain sebagai argumen input, dan mengembalikan ...

// 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, apakah kode Anda secara fungsional berbeda int usageCnt = nrgDel.Invoke(lastRunTime, procDT, null, null);? Sepertinya itu menangguhkan utas saat ini dengan tidur ... Saya pikir itu tidak akan membantu apa pun dengan pembekuan GUI jika Anda menyebutnya di utas GUI
IgorK

Ya, BeginInvoke memanggil delegasi pada utas lain dari utas kumpulan ... Jika Anda menggunakan Invoke, delegasi disebut sinkron pada utas saat ini ... Tapi Anda benar, tidurnya salah ... Anda harus menghilangkan itu dan kumpulkan hasil menggunakan fungsi panggilan balik. Saya akan mengedit untuk menunjukkan bahwa
Charles Bretana

1

Ada banyak cara menjalankan utas terpisah di .Net, masing-masing memiliki perilaku yang berbeda. Apakah Anda perlu terus menjalankan utas setelah GUI berhenti? Apakah Anda perlu menyampaikan informasi antara utas dan GUI? Apakah utas perlu memperbarui GUI? Haruskah utas melakukan satu tugas kemudian berhenti, atau haruskah terus berjalan? Jawaban atas pertanyaan-pertanyaan ini akan memberi tahu Anda metode mana yang digunakan.

Ada artikel metode async yang bagus di situs web Proyek Code yang menjelaskan berbagai metode dan memberikan kode sampel.

Catatan artikel ini ditulis sebelum pola async / await dan Task Parallel Library diperkenalkan ke .NET.


Ini adalah jenis info yang saya cari, tetapi jawaban Anda adalah korban tautan busuk.
Chris

Terima kasih telah memberi tahu saya, @ Chris; tautan diperbaiki.
Dour High Arch

0

Cara: Gunakan Thread Latar Belakang untuk Mencari File

Anda harus sangat berhati-hati dengan akses dari utas lain ke hal-hal spesifik GUI (ini umum untuk banyak toolkit GUI). Jika Anda ingin memperbarui sesuatu dalam GUI dari pemrosesan utas, periksa jawaban ini yang menurut saya berguna untuk WinForms. Untuk WPF lihat ini (ini menunjukkan bagaimana menyentuh komponen dalam metode UpdateProgress () sehingga akan bekerja dari utas lainnya, tapi sebenarnya saya tidak suka itu tidak dilakukan CheckAccess()sebelum melakukan BeginInvokemelalui Dispathcer, lihat dan cari CheckAccess di dalamnya)

Sedang mencari .NET buku khusus tentang threading dan menemukan ini (dapat diunduh gratis). Lihat http://www.albahari.com/threading/ untuk perincian lebih lanjut tentang itu.

Saya yakin Anda akan menemukan apa yang Anda butuhkan untuk menjalankan eksekusi sebagai utas baru di 20 halaman pertama dan memiliki lebih banyak (tidak yakin tentang cuplikan khusus GUI yang saya maksud khusus untuk threading). Senang mendengar apa pendapat masyarakat tentang pekerjaan ini karena saya membaca yang ini. Untuk sekarang tampak cukup rapi untuk saya (untuk menunjukkan metode dan jenis NET spesifik untuk threading). Juga mencakup .NET 2.0 (dan bukan 1.1 kuno) apa yang saya sangat hargai.


0

Saya akan merekomendasikan melihat Power Threading Library milik Jeff Richter dan khususnya IAsyncEnumerator. Lihatlah videonya di Charlie Calvert's blog mana Richter memeriksanya untuk ikhtisar yang bagus.

Jangan menunda namanya karena itu membuat tugas pemrograman yang tidak sinkron lebih mudah dikodekan.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.