Bagaimana cara meneruskan parameter ke metode ThreadStart di Thread?

291

Bagaimana cara mengirimkan parameter ke Thread.ThreadStart()metode dalam C #?

Misalkan saya memiliki metode yang disebut 'unduh'

public void download(string filename)
{
    // download code
}

Sekarang saya telah membuat satu utas dalam metode utama:

Thread thread = new Thread(new ThreadStart(download(filename));

jenis metode kesalahan yang diharapkan.

Bagaimana saya bisa meneruskan parameter ThreadStartdengan metode target dengan parameter?

Swapnil Gupta
sumber
2
Lihat artikel ini yang ditulis oleh Jon Skeet Bagian Parameter ada di halaman berikutnya tetapi artikel secara keseluruhan adalah bacaan yang cukup bagus.
codingbadger

Jawaban:

696

Yang paling sederhana adalah adil

string filename = ...
Thread thread = new Thread(() => download(filename));
thread.Start();

Keuntungan dari ini (lebih ParameterizedThreadStart) adalah bahwa Anda dapat melewati beberapa parameter, dan Anda mendapatkan pemeriksaan waktu kompilasi tanpa harus dilemparkan dari objectsemua waktu.

Marc Gravell
sumber
15
Saya minta maaf untuk offtopic tetapi apa artinya operator '()'? Saya melihatnya kadang-kadang tetapi saya tidak punya waktu untuk memeriksa.
ŁukaszW.pl
24
Ini adalah ekspresi lambda tanpa argumen.
Noldorin
31
@ ŁukaszW.pl - apa yang dikatakan Noldorin; p dalam C # 2.0 alternatif konstruksi (untuk contoh ini) adalahnew Thread(delegate() { download(filename); });
Marc Gravell
7
@Tymek itu tidak cukup akurat; setiap variabel yang ditangkap diperlakukan sebagai penutupan leksikal penuh , yang (sebagai detail implementasi) diimplementasikan sebagai bidang pada kelas yang dihasilkan kompiler. Selain itu, ruang lingkup penutupan didefinisikan sebagai ruang lingkup deklarasi. Itu tidak benar-benar "sebagai referensi" seperti itu ("lewat referensi" dan "tipe referensi" keduanya didefinisikan dengan baik, dan tidak ada yang benar-benar menggambarkan skenario ini)
Marc Gravell
5
@MarcGravell - Anda benar. Yang harus saya katakan adalah bahwa orang harus sadar bahwa jika 'nama file' berubah sebelum utas dimulai, nilai baru akan digunakan. Saya seharusnya tidak mengoceh tentang mekanisme itu dan saya pasti tidak boleh berbicara tentang referensi.
tymtam
36

Lihat contoh ini:

public void RunWorker()
{
    Thread newThread = new Thread(WorkerMethod);
    newThread.Start(new Parameter());
}

public void WorkerMethod(object parameterObj)
{
    var parameter = (Parameter)parameterObj;
    // do your job!
}

Anda pertama-tama membuat utas dengan mengirimkan delegasi ke metode pekerja dan kemudian memulainya dengan metode Thread.Start yang menjadikan objek Anda sebagai parameter.

Jadi dalam kasus Anda, Anda harus menggunakannya seperti ini:

    Thread thread = new Thread(download);
    thread.Start(filename);

Tetapi metode 'unduh' Anda masih perlu mengambil objek , bukan string sebagai parameter. Anda bisa melemparkannya ke string di tubuh metode Anda.

ŁukaszW.pl
sumber
25

Anda ingin menggunakan ParameterizedThreadStartdelegasi untuk metode utas yang mengambil parameter. (Atau tidak ada sama sekali sebenarnya, dan biarkan Threadkonstruktor menyimpulkan.)

Contoh penggunaan:

var thread = new Thread(new ParameterizedThreadStart(download));
//var thread = new Thread(download); // equivalent

thread.Start(filename)
Noldorin
sumber
7

Anda juga bisa delegateseperti itu ...

ThreadStart ts = delegate
{
      bool moreWork = DoWork("param1", "param2", "param3");
      if (moreWork) 
      {
          DoMoreWork("param1", "param2");
      }
};
new Thread(ts).Start();
Tuan Mick
sumber
4

Sebagai tambahan

    Thread thread = new Thread(delegate() { download(i); });
    thread.Start();
Metin Atalay
sumber
3

Anda dapat merangkum fungsi utas (unduh) dan parameter yang diperlukan (nama berkas) di kelas dan menggunakan delegasi ThreadStart untuk menjalankan fungsi utas.

public class Download
{
    string _filename;

    Download(string filename)
    {
       _filename = filename;
    }

    public void download(string filename)
    {
       //download code
    }
}

Download = new Download(filename);
Thread thread = new Thread(new ThreadStart(Download.download);
Jackypengyu
sumber
Saya suka pendekatan ini jauh lebih baik, saya menemukan bahwa pendekatan ekspresi lambda tidak selalu melacak parameter yang tepat
meanbunny
3

Saya akan merekomendasikan Anda untuk memiliki kelas lain yang disebut File.

public class File
{
   private string filename;

   public File(string filename)
   {
      this.filename= filename;
   }

   public void download()
   {
       // download code using filename
   }
}

Dan dalam kode pembuatan utas Anda, Anda membuat instance file baru:

string filename = "my_file_name";

myFile = new File(filename);

ThreadStart threadDelegate = new ThreadStart(myFile.download);

Thread newThread = new Thread(threadDelegate);
João Pedro Andrade Marques
sumber
0

Bagaimana dengan ini: (atau apakah boleh menggunakan seperti ini?)

var test = "Hello";
new Thread(new ThreadStart(() =>
{
    try
    {
        //Staff to do
        Console.WriteLine(test);
    }
    catch (Exception ex)
    {
        throw;
    }
})).Start();
Cansın Şenalioğlu
sumber
-1

Menurut pertanyaan Anda ...

Bagaimana cara mengirimkan parameter ke metode Thread.ThreadStart () di C #?

... dan kesalahan yang Anda temui, Anda harus memperbaiki kode Anda dari

Thread thread = new Thread(new ThreadStart(download(filename));

untuk

Thread thread = new Thread(new ThreadStart(download));
thread.Start(filename);



Namun, pertanyaannya lebih kompleks seperti pada awalnya.

The Threadkelas saat (4.7.2) menyediakan beberapa konstruktor dan Startmetode dengan overloads.

Konstruktor yang relevan untuk pertanyaan ini adalah:

public Thread(ThreadStart start);

dan

public Thread(ParameterizedThreadStart start);

baik yang mengambil ThreadStartdelegasi atau ParameterizedThreadStartdelegasi.

Delegasi yang sesuai terlihat seperti ini:

public delegate void ThreadStart();
public delegate void ParameterizedThreadStart(object obj);

Jadi seperti yang bisa dilihat, konstruktor yang tepat untuk digunakan tampaknya adalah orang yang mengambil ParameterizedThreadStartdelegasi sehingga beberapa metode sesuai dengan tanda tangan yang ditentukan dari delegasi dapat dimulai oleh utas.

Contoh sederhana untuk instanciating Threadkelas akan menjadi

Thread thread = new Thread(new ParameterizedThreadStart(Work));

atau hanya

Thread thread = new Thread(Work);

Tanda tangan dari metode yang sesuai (disebut Workdalam contoh ini) terlihat seperti ini:

private void Work(object data)
{
   ...
}

Yang tersisa adalah memulai utas. Ini dilakukan dengan menggunakan keduanya

public void Start();

atau

public void Start(object parameter);

Sementara Start()akan memulai utas dan meneruskan nullsebagai data ke metode, Start(...)dapat digunakan untuk melewatkan apa pun ke dalam Workmetode utas.

Namun ada satu masalah besar dengan pendekatan ini: Segala sesuatu yang dilewatkan ke dalam Workmetode dilemparkan ke objek. Itu berarti dalam Workmetode itu harus dilemparkan ke tipe asli lagi seperti pada contoh berikut:

public static void Main(string[] args)
{
    Thread thread = new Thread(Work);

    thread.Start("I've got some text");
    Console.ReadLine();
}

private static void Work(object data)
{
    string message = (string)data; // Wow, this is ugly

    Console.WriteLine($"I, the thread write: {message}");
}



Casting adalah sesuatu yang biasanya tidak ingin Anda lakukan.

Bagaimana jika seseorang melewati sesuatu yang bukan string? Karena ini kelihatannya tidak mungkin pada awalnya (karena ini adalah metode saya, saya tahu apa yang saya lakukan atau metode ini bersifat pribadi, bagaimana mungkin seseorang bisa memberikan apa pun padanya? ) Anda mungkin berakhir dengan kasus seperti itu karena berbagai alasan . Karena beberapa kasus mungkin tidak menjadi masalah, yang lain juga. Dalam kasus seperti itu, Anda mungkin akan berakhir dengan sesuatu InvalidCastExceptionyang mungkin tidak akan Anda perhatikan karena itu hanya mengakhiri utas.

Sebagai solusi Anda akan mengharapkan untuk mendapatkan ParameterizedThreadStartdelegasi generik seperti di ParameterizedThreadStart<T>mana Takan menjadi tipe data yang ingin Anda sampaikan ke dalam Workmetode. Sayangnya sesuatu seperti ini belum ada (belum?).

Namun ada solusi yang disarankan untuk masalah ini. Ini melibatkan pembuatan kelas yang berisi keduanya, data yang akan diteruskan ke utas serta metode yang mewakili metode pekerja seperti ini:

public class ThreadWithState
{
    private string message;

    public ThreadWithState(string message)
    {
        this.message = message;
    }

    public void Work()
    {
        Console.WriteLine($"I, the thread write: {this.message}");
    }
}

Dengan pendekatan ini Anda akan memulai utas seperti ini:

ThreadWithState tws = new ThreadWithState("I've got some text");
Thread thread = new Thread(tws.Work);

thread.Start();

Jadi dengan cara ini Anda cukup menghindari casting dan memiliki cara yang aman untuk memberikan data ke utas ;-)

Markus Safar
sumber
-2

di sini adalah cara yang sempurna ...

private void func_trd(String sender)
{

    try
    {
        imgh.LoadImages_R_Randomiz(this, "01", groupBox, randomizerB.Value); // normal code

        ThreadStart ts = delegate
        {
            ExecuteInForeground(sender);
        };

        Thread nt = new Thread(ts);
        nt.IsBackground = true;

        nt.Start();

    }
    catch (Exception)
    {

    }
}

private void ExecuteInForeground(string name)
{
     //whatever ur function
    MessageBox.Show(name);
}
Aylian Craspa
sumber