Wie übergebe ich Parameter an die ThreadStart-Methode in Thread?

291

Wie übergebe ich Parameter an die Thread.ThreadStart()Methode in C #?

Angenommen, ich habe eine Methode namens "Download".

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

Jetzt habe ich einen Thread in der Hauptmethode erstellt:

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

Fehlermethode Typ erwartet.

Wie kann ich Parameter ThreadStartmit der Zielmethode mit Parametern übergeben?

Swapnil Gupta
quelle
2
Lesen Sie diesen Artikel von Jon Skeet. Der Abschnitt Parameter befindet sich auf der nächsten Seite, aber der Artikel als Ganzes ist eine ziemlich gute Lektüre.
Codingbadger

Antworten:

696

Das einfachste ist einfach

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

Der Vorteil (der Vorteile) dieses (Over ParameterizedThreadStart) besteht darin, dass Sie mehrere Parameter übergeben können und eine Überprüfung der Kompilierungszeit erhalten, ohne objectständig umwandeln zu müssen .

Marc Gravell
quelle
15
Es tut mir leid für offtopic, aber was bedeutet der Operator '()'? Ich sehe es manchmal, aber ich habe keine Zeit zu überprüfen.
ŁukaszW.pl
24
Es ist ein Lambda-Ausdruck ohne Argumente.
Noldorin
31
@ ŁukaszW.pl - was Noldorin sagte; p in C # 2.0 ist ein alternatives Konstrukt (für dieses Beispiel)new Thread(delegate() { download(filename); });
Marc Gravell
7
@Tymek das ist nicht ganz richtig; Alle erfassten Variablen werden als vollständige lexikalische Abschlüsse behandelt , die (als Implementierungsdetail) als Felder in einer vom Compiler generierten Klasse implementiert werden. Darüber hinaus wird der Abschlussbereich als Deklarationsbereich definiert. Es ist nicht wirklich "als Referenz" als solche ("Referenzübergabe" und "Referenztypen" sind beide gut definiert und beschreiben dieses Szenario auch nicht wirklich)
Marc Gravell
5
@MarcGravell - Sie sind richtig. Ich hätte nur sagen sollen, dass man sich bewusst sein sollte, dass der neue Wert verwendet wird, wenn sich der Dateiname vor dem Start des Threads ändert. Ich hätte nicht über die Mechanik davon plappern sollen und ich sollte definitiv nicht über Referenzierung sprechen.
Tymtam
36

Schauen Sie sich dieses Beispiel an:

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

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

Sie erstellen zuerst einen Thread, indem Sie den Delegaten an die Worker-Methode übergeben und ihn dann mit einer Thread.Start-Methode starten, die Ihr Objekt als Parameter verwendet.

In Ihrem Fall sollten Sie es also folgendermaßen verwenden:

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

Ihre 'Download'-Methode muss jedoch weiterhin ein Objekt und keine Zeichenfolge als Parameter verwenden. Sie können es in einen String in Ihrem Methodenkörper umwandeln.

ŁukaszW.pl
quelle
25

Sie möchten den ParameterizedThreadStartDelegaten für Thread-Methoden verwenden, die Parameter annehmen. (Oder gar keine, und lassen Sie den ThreadKonstruktor schließen.)

Anwendungsbeispiel:

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

thread.Start(filename)
Noldorin
quelle
7

Das könnte dir auch delegategefallen ...

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

In Zusätzlich

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

Sie können die Thread-Funktion (Download) und die erforderlichen Parameter (Dateiname) in einer Klasse kapseln und den ThreadStart-Delegaten verwenden, um die Thread-Funktion auszuführen.

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
quelle
Ich mag diesen Ansatz viel besser, ich fand, dass der Lambda-Ausdrucksansatz nicht immer die richtigen Parameter verfolgt
meanbunny
3

Ich würde Ihnen empfehlen, eine andere Klasse namens File zu haben.

public class File
{
   private string filename;

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

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

Und in Ihrem Thread-Erstellungscode instanziieren Sie eine neue Datei:

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
quelle
0

Wie wäre es damit: (oder ist es in Ordnung, so zu verwenden?)

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

Nach Ihrer Frage ...

Wie übergebe ich Parameter an die Thread.ThreadStart () -Methode in C #?

... und den Fehler, auf den Sie gestoßen sind, müssten Sie Ihren Code von korrigieren

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

zu

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



Die Frage ist jedoch komplexer, wie es zunächst scheint.

Die ThreadKlasse (4.7.2) bietet derzeit mehrere Konstruktoren und eine StartMethode mit Überladungen.

Diese relevanten Konstruktoren für diese Frage sind:

public Thread(ThreadStart start);

und

public Thread(ParameterizedThreadStart start);

die entweder einen ThreadStartDelegierten oder einen ParameterizedThreadStartDelegierten nehmen.

Die entsprechenden Delegierten sehen folgendermaßen aus:

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

Wie zu sehen ist, scheint der richtige Konstruktor derjenige zu sein, der einen ParameterizedThreadStartDelegaten nimmt, so dass eine Methode, die der angegebenen Signatur des Delegaten entspricht, vom Thread gestartet werden kann.

Ein einfaches Beispiel für die Instanziierung der ThreadKlasse wäre

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

oder nur

Thread thread = new Thread(Work);

Die Signatur der entsprechenden Methode ( Workin diesem Beispiel aufgerufen ) sieht folgendermaßen aus:

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

Was bleibt, ist den Thread zu starten. Dies erfolgt entweder mit

public void Start();

oder

public void Start(object parameter);

Während Start()der Thread gestartet und nullals Daten an die Methode übergeben würde, Start(...)kann verwendet werden, um alles an die WorkMethode des Threads zu übergeben.

Bei diesem Ansatz gibt es jedoch ein großes Problem: Alles, was an die WorkMethode übergeben wird, wird in ein Objekt umgewandelt. Das heißt, innerhalb der WorkMethode muss es wieder wie im folgenden Beispiel in den ursprünglichen Typ umgewandelt werden:

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 ist etwas, was Sie normalerweise nicht wollen.

Was ist, wenn jemand etwas anderes übergibt, das keine Zeichenfolge ist? Da dies zunächst nicht möglich zu sein scheint (weil es meine Methode ist, ich weiß, was ich tue oder die Methode privat ist, wie sollte jemand jemals etwas an sie weitergeben können? ), Können Sie möglicherweise aus verschiedenen Gründen genau diesen Fall haben . Da einige Fälle möglicherweise kein Problem darstellen, sind es andere. In solchen Fällen werden Sie wahrscheinlich mit einem endenInvalidCastException die Sie wahrscheinlich nicht bemerken werden, weil sie den Thread einfach beendet.

Als Lösung würden erwarten , dass Sie ein generisch erhalten ParameterizedThreadStartDelegierten wie , ParameterizedThreadStart<T>wo Tdie Daten der Typ , der Sie in denen geben wollen wäre WorkMethode. Leider gibt es so etwas (noch?) Nicht.

Es gibt jedoch eine vorgeschlagene Lösung für dieses Problem. Dabei wird eine Klasse erstellt, die sowohl die an den Thread zu übergebenden Daten als auch die Methode enthält, die die Arbeitsmethode wie folgt darstellt:

public class ThreadWithState
{
    private string message;

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

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

Mit diesem Ansatz würden Sie den Thread folgendermaßen starten:

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

thread.Start();

Auf diese Weise vermeiden Sie einfach das Herumwirbeln und haben eine typsichere Möglichkeit, Daten für einen Thread bereitzustellen ;-)

Markus Safar
quelle
-2

Hier ist der perfekte Weg ...

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
quelle