Comment passer des paramètres à la méthode ThreadStart dans Thread?

291

Comment passer des paramètres à Thread.ThreadStart() méthode en C #?

Supposons que j'ai une méthode appelée «téléchargement»

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

Maintenant, j'ai créé un fil dans la méthode principale:

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

type de méthode d'erreur attendu.

Comment puis-je transmettre des paramètres ThreadStartavec la méthode cible avec des paramètres?

Swapnil Gupta
la source
2
Consultez cet article écrit par Jon Skeet La section Paramètres est sur la page suivante mais l'article dans son ensemble est une assez bonne lecture.
codingbadger

Réponses:

696

Le plus simple est juste

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

Le (s) avantage (s) de ce (sur ParameterizedThreadStart) est que vous pouvez passer plusieurs paramètres, et vous obtenez une vérification au moment de la compilation sans avoir besoin de transtyper à objecttout moment.

Marc Gravell
la source
15
Je suis désolé pour offtopic mais que signifie l'opérateur '()'? Je le vois parfois mais je n'ai pas le temps de vérifier.
ŁukaszW.pl
24
C'est une expression lambda sans argument.
Noldorin
31
@ ŁukaszW.pl - ce que Noldorin a dit; p en C # 2.0 une construction alternative (pour cet exemple) estnew Thread(delegate() { download(filename); });
Marc Gravell
7
@Tymek qui n'est pas tout à fait exact; toutes les variables capturées sont traitées comme des fermetures lexicales complètes qui (en tant que détail d'implémentation) sont implémentées en tant que champs dans une classe générée par le compilateur. En outre, la portée de fermeture est définie comme étant la portée de déclaration. Ce n'est pas vraiment "en tant que références" en tant que telles ("passer par référence" et "types de référence" sont tous deux bien définis, et ni l'un ni l'autre ne décrit vraiment ce scénario)
Marc Gravell
5
@MarcGravell - vous avez raison. Tout ce que j'aurais dû dire, c'est qu'il faut savoir que si le «nom de fichier» change avant le démarrage du thread, la nouvelle valeur sera utilisée. Je n'aurais pas dû bavarder sur la mécanique de celui-ci et je ne devrais certainement pas parler de référencement.
tymtam
36

Regardez cet exemple:

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

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

Vous créez d'abord un thread en passant délégué à la méthode de travail, puis le démarre avec une méthode Thread.Start qui prend votre objet en paramètre.

Donc, dans votre cas, vous devez l'utiliser comme ceci:

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

Mais votre méthode de téléchargement doit toujours prendre un objet , pas une chaîne comme paramètre. Vous pouvez le convertir en chaîne dans votre corps de méthode.

ŁukaszW.pl
la source
25

Vous souhaitez utiliser le ParameterizedThreadStartdélégué pour les méthodes de threads qui prennent des paramètres. (Ou pas du tout en fait, et laissez leThread constructeur en déduire.)

Exemple d'utilisation:

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

thread.Start(filename)
Noldorin
la source
7

Vous pourriez aussi delegateaimer ainsi ...

ThreadStart ts = delegate
{
      bool moreWork = DoWork("param1", "param2", "param3");
      if (moreWork) 
      {
          DoMoreWork("param1", "param2");
      }
};
new Thread(ts).Start();
Maître Mick
la source
4

De plus

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

Vous pouvez encapsuler la fonction de thread (téléchargement) et les paramètres nécessaires (nom de fichier) dans une classe et utiliser le délégué ThreadStart pour exécuter la fonction de thread.

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
la source
J'aime beaucoup mieux cette approche, j'ai trouvé que l'approche de l'expression lambda ne suit pas toujours les bons paramètres
meanbunny
3

Je vous recommanderais d'avoir une autre classe appelée File.

public class File
{
   private string filename;

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

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

Et dans votre code de création de threads, vous instanciez un nouveau fichier:

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
la source
0

Que diriez-vous de ceci: (ou est-ce correct d'utiliser comme ça?)

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

Selon votre question ...

Comment passer des paramètres à la méthode Thread.ThreadStart () en C #?

... et l'erreur que vous avez rencontrée, vous devrez corriger votre code à partir de

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

à

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



Cependant, la question est plus complexe qu'elle semble au premier abord.

La Threadclasse actuellement (4.7.2) fournit plusieurs constructeurs et une Startméthode avec des surcharges.

Ces constructeurs pertinents pour cette question sont:

public Thread(ThreadStart start);

et

public Thread(ParameterizedThreadStart start);

qui soit prendre un ThreadStartdélégué ou un ParameterizedThreadStartdélégué.

Les délégués correspondants ressemblent à ceci:

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

Ainsi, comme on peut le voir, le constructeur correct à utiliser semble être celui qui prend un ParameterizedThreadStartdélégué afin qu'une certaine méthode conforme à la signature spécifiée du délégué puisse être démarrée par le thread.

Un exemple simple d'instanciation de la Threadclasse serait

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

ou juste

Thread thread = new Thread(Work);

La signature de la méthode correspondante (appelée Workdans cet exemple) ressemble à ceci:

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

Ce qui reste est de démarrer le fil. Cela se fait en utilisant soit

public void Start();

ou

public void Start(object parameter);

Alors Start()que démarrerait le thread et passerait nullcomme données à la méthode, Start(...)peut être utilisé pour passer n'importe quoi dans leWork méthode du thread.

Il y a cependant un gros problème avec cette approche: tout ce qui est passé dans la Workméthode est converti en objet. Cela signifie que dans la Workméthode, il doit à nouveau être converti en type d'origine, comme dans l'exemple suivant:

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



Le casting est quelque chose que vous ne voulez généralement pas faire.

Et si quelqu'un passe quelque chose d'autre qui n'est pas une chaîne? Comme cela ne semble pas possible au premier abord (parce que c'est ma méthode, je sais ce que je fais ou la méthode est privée, comment quelqu'un devrait-il pouvoir lui transmettre quoi que ce soit? ), Vous pouvez éventuellement vous retrouver exactement avec ce cas pour diverses raisons . Comme certains cas peuvent ne pas être un problème, d'autres le sont. Dans de tels cas, vous vous retrouverez probablement avec unInvalidCastException que vous ne remarquerez probablement pas car il termine simplement le thread.

Comme solution, vous vous attendez à obtenir un ParameterizedThreadStartdélégué générique comme ParameterizedThreadStart<T>T serait le type de données que vous souhaitez transmettre dans leWork méthode. Malheureusement, quelque chose comme ça n'existe pas (encore?).

Il existe cependant une solution suggérée à ce problème. Cela implique de créer une classe qui contient les deux, les données à transmettre au thread ainsi que la méthode qui représente la méthode de travail comme ceci:

public class ThreadWithState
{
    private string message;

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

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

Avec cette approche, vous commenceriez le fil comme ceci:

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

thread.Start();

Donc, de cette façon, vous évitez simplement de lancer et d'avoir un moyen sûr de fournir des données à un thread ;-)

Markus Safar
la source
-2

voici le moyen parfait ...

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
la source