Thread'deki ThreadStart yöntemine parametreler nasıl aktarılır?


291

Parametreleri Thread.ThreadStart()C # yöntemine geçirmek nasıl ?

'Download' adında bir yöntemim olduğunu varsayalım

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

Şimdi ana yöntemde bir iş parçacığı oluşturduk:

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

hata yöntemi türü bekleniyor.

Parametreleri ThreadStartolan hedef yöntemle parametreleri nasıl iletebilirim ?


2
Jon Skeet tarafından yazılan bu makaleye göz atın Parametreler bölümü bir sonraki sayfadadır, ancak bir bütün olarak makale oldukça iyi okunur.
codingbadger

Yanıtlar:


697

En basiti sadece

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

Bunun avantajı / avantajları ParameterizedThreadStart, birden fazla parametre geçirebilmeniz ve her zaman yayın yapmanıza gerek kalmadan derleme zamanı denetimi almanızdır object.


15
Offtopic için özür dilerim ama '()' operatörü ne anlama geliyor? Bazen görüyorum ama kontrol edecek zamanım yok.
asukaszW.pl

24
Tartışmasız bir lambda ifadesi.
Noldorin

31
@ AsukaszW.pl - Noldorin'in söyledikleri; C # 2.0'daki p (bu örnek için) alternatif bir yapınew Thread(delegate() { download(filename); });
Marc Gravell

7
@Tymek bu pek doğru değil ; yakalanan tüm değişkenler , derleyici tarafından oluşturulan bir sınıftaki alanlar olarak uygulanan (uygulama ayrıntısı olarak) tam sözcüksel kapaklar olarak ele alınır . Ayrıca, kapanış kapsamı, bildirim kapsamı olarak tanımlanır. Gerçekten "referans olarak" değil ("referansla geç" ve "referans türleri" her ikisi de iyi tanımlanmış ve ikisi de bu senaryoyu gerçekten açıklamıyor)
Marc Gravell

5
@MarcGravell - haklısın. Tüm söylemeliyim bir iş parçacığı başlamadan önce 'dosya adı' değişirse yeni değer kullanılacak farkında olmalıdır. Ben onun mekaniği hakkında konuşmamalıydım ve kesinlikle referanslardan bahsetmemeliydim.
Mart'ta timtam

37

Şu örneğe bakın:

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

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

İlk olarak delegate'i worker (işçi) yöntemine geçirerek bir iş parçacığı oluşturuyorsunuz ve sonra bunu nesne olarak parametre olarak alan bir Thread.Start yöntemiyle başlıyorsunuz.

Yani sizin durumunuzda böyle kullanmalısınız:

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

Ancak 'download' yönteminizin parametre olarak dizeyi değil, nesneyi alması gerekir . Yöntem gövgenizde dizeye yayınlayabilirsiniz.


25

ParameterizedThreadStartParametreleri alan iş parçacığı yöntemleri için temsilci kullanmak istiyorsunuz . (Ya da aslında hiçbiri ve Threadkurucunun çıkarımda bulunmasına izin verin .)

Örnek kullanım:

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

thread.Start(filename)

7

Şunu da delegatebeğenebilirsin ...

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

4

Ek olarak

    Thread thread = new Thread(delegate() { download(i); });
    thread.Start();

3

Bir sınıfta thread işlevini (download) ve gerekli parametreleri (dosya adı) kapsülleyebilir ve thread işlevini yürütmek için ThreadStart temsilcisini kullanabilirsiniz.

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

Bu yaklaşımı çok daha çok sevdim, lambda ifade yaklaşımının her zaman doğru parametreleri takip
etmediğini buldum

3

File adında başka bir sınıfa sahip olmanızı öneririm.

public class File
{
   private string filename;

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

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

İş parçacığı oluşturma kodunuzda yeni bir dosya başlatırsınız:

string filename = "my_file_name";

myFile = new File(filename);

ThreadStart threadDelegate = new ThreadStart(myFile.download);

Thread newThread = new Thread(threadDelegate);

0

Buna ne dersiniz: (ya da böyle kullanmak uygun mu?)

var test = "Hello";
new Thread(new ThreadStart(() =>
{
    try
    {
        //Staff to do
        Console.WriteLine(test);
    }
    catch (Exception ex)
    {
        throw;
    }
})).Start();

-1

Sorunuza göre ...

Parametreleri C # 'da Thread.ThreadStart () yöntemine geçirmek nasıl?

... ve karşılaştığınız hatayı, kodunuzu

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

için

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



Ancak, soru ilk bakışta göründüğü gibi daha karmaşıktır.

ThreadSınıfı şu anda (4.7.2) birçok içerir kurucular ve Startaşırı yük yöntemi.

Bu soru için bu ilgili kurucular:

public Thread(ThreadStart start);

ve

public Thread(ParameterizedThreadStart start);

ya bir ThreadStartdelege ya da bir ParameterizedThreadStartdelege alır.

Karşılık gelen delegeler şöyle görünür:

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

Görülebileceği gibi, kullanılacak doğru kurucu bir ParameterizedThreadStarttemsilci alan kişi gibi görünmektedir, böylece temsilci tarafından belirtilen imzayla uyumlu bazı yöntemler iş parçacığı tarafından başlatılabilir.

ThreadSınıfı örneklemek için basit bir örnek

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

ya da sadece

Thread thread = new Thread(Work);

İlgili yöntemin imzası ( Workbu örnekte denir ) şöyle görünür:

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

Geriye kalan, iş parçacığını başlatmaktır. Bu, aşağıdakilerden birini kullanarak yapılır:

public void Start();

veya

public void Start(object parameter);

İken Start()açtığınızda, gerekli geçerdi nullyönteme veri olarak, Start(...)geçmek kullanılabilir şey içine Workiplik yöntemi.

Ancak bu yaklaşımda büyük bir sorun var: WorkYönteme aktarılan her şey bir nesneye dökülüyor. Bu, Workyöntem içinde, aşağıdaki örnekte olduğu gibi tekrar orijinal türe çevrilmesi gerektiği anlamına gelir :

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



Döküm genellikle yapmak istemediğiniz bir şeydir.

Birisi dize olmayan başka bir şey geçerse ne olur? Bu ilk başta mümkün görünmediği için (çünkü bu benim yöntemim, ne yaptığımı biliyorum veya yöntem özel, birisi ona herhangi bir şeyi nasıl geçebilir? ) Muhtemelen çeşitli nedenlerden dolayı tam olarak bu dava ile sonuçlanabilir . Bazı vakalar sorun yaratmayabileceğinden diğerleri. Bu gibi durumlarda, muhtemelen InvalidCastExceptioniş parçacığını sonlandırdığı için muhtemelen fark etmeyeceğiniz bir sonuç alırsınız.

Bir çözüm olarak bir jenerik olsun beklenir ParameterizedThreadStartgibi temsilci ParameterizedThreadStart<T>nereye Tsen geçirmek istediğiniz türde veriler olacağını Workyöntemle. Ne yazık ki böyle bir şey henüz yok (henüz?).

Ancak , bu soruna önerilen bir çözüm vardır . İş parçacığına iletilecek verileri ve bunun gibi işçi yöntemini temsil eden yöntemi içeren bir sınıf oluşturmayı içerir:

public class ThreadWithState
{
    private string message;

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

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

Bu yaklaşımla iş parçacığını şöyle başlatacaksınız:

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

thread.Start();

Böylece bu şekilde döküm yapmaktan kaçınır ve bir iş parçacığına veri sağlamanın tipik bir yoluna sahip olursunuz ;-)


-2

İşte mükemmel yol ...

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