Comment démarrer un thread avec des paramètres en C #?
Comment démarrer un thread avec des paramètres en C #?
Réponses:
Ouais:
Thread t = new Thread (new ParameterizedThreadStart(myMethod));
t.Start (myParameterObject);
void MyParamObject(object myUrl){ //do stuff }
devrait avoir le type de paramètreobject
ParameterizedThreadStart
et clairement à partir du texte de la question, ce n'est probablement pas le cas.
L'une des 2 surcharges du constructeur Thread utilise un délégué ParameterizedThreadStart qui vous permet de passer un seul paramètre à la méthode de démarrage. Malheureusement, il ne permet qu'un seul paramètre et il le fait de manière dangereuse car il le passe en tant qu'objet. Je trouve qu'il est beaucoup plus facile d'utiliser une expression lambda pour capturer les paramètres pertinents et les transmettre de manière fortement typée.
Essayez ce qui suit
public Thread StartTheThread(SomeType param1, SomeOtherType param2) {
var t = new Thread(() => RealStart(param1, param2));
t.Start();
return t;
}
private static void RealStart(SomeType param1, SomeOtherType param2) {
...
}
Dim thr As New Thread(Sub() DoStuff(settings))
Vous pouvez utiliser des expressions lambda
private void MyMethod(string param1,int param2)
{
//do stuff
}
Thread myNewThread = new Thread(() => MyMethod("param1",5));
myNewThread.Start();
c'est jusqu'à présent la meilleure réponse que j'ai pu trouver, c'est rapide et facile.
Thread thread = new Thread(Work);
thread.Start(Parameter);
private void Work(object param)
{
string Parameter = (string)param;
}
Le type de paramètre doit être un objet.
ÉDITER:
Bien que cette réponse ne soit pas incorrecte, je déconseille cette approche. L'utilisation d'une expression lambda est beaucoup plus facile à lire et ne nécessite pas de transtypage de type. Voir ici: https://stackoverflow.com/a/1195915/52551
Parameter
?
class Program
{
static void Main(string[] args)
{
Thread t = new Thread(new ParameterizedThreadStart(ThreadMethod));
t.Start("My Parameter");
}
static void ThreadMethod(object parameter)
{
// parameter equals to "My Parameter"
}
}
Un moyen simple d'utiliser lambda comme ça ..
Thread t = new Thread(() => DoSomething("param1", "param2"));
t.Start();
OU vous pourriez même delegate
utiliser ThreadStart
comme ça ...
ThreadStart ts = delegate
{
bool moreWork = DoWork("param1", "param2", "param3");
if (moreWork)
{
DoMoreWork("param4", "param5");
}
};
new Thread(ts).Start();
OU en utilisant VS 2019 .NET 4.5+ encore plus propre comme ça ..
private void DoSomething(int param1, string param2)
{
//DO SOMETHING..
void ts()
{
if (param1 > 0) DoSomethingElse(param2, "param3");
}
new Thread(ts).Start();
//DO SOMETHING..
}
Utilisez ParametrizedThreadStart
.
Comme cela a déjà été mentionné dans diverses réponses ici, la Thread
classe actuellement (4.7.2) fournit plusieurs constructeurs et une Start
méthode avec des surcharges.
Ces constructeurs pertinents pour cette question sont:
public Thread(ThreadStart start);
et
public Thread(ParameterizedThreadStart start);
qui soit prendre un ThreadStart
délégué ou un ParameterizedThreadStart
délégué.
Les délégués correspondants ressemblent à ceci:
public delegate void ThreadStart();
public delegate void ParameterizedThreadStart(object obj);
Donc, comme on peut le voir, le constructeur correct à utiliser semble être celui qui prend un ParameterizedThreadStart
dé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 Thread
classe serait
Thread thread = new Thread(new ParameterizedThreadStart(Work));
ou juste
Thread thread = new Thread(Work);
La signature de la méthode correspondante (appelée Work
dans 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 null
comme données à la méthode, Start(...)
peut être utilisé pour passer n'importe quoi dans la Work
méthode du thread.
Il y a cependant un gros problème avec cette approche: tout ce qui est passé dans la Work
méthode est converti en objet. Cela signifie que dans la Work
mé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 début (car 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 ParameterizedThreadStart
délégué générique comme ParameterizedThreadStart<T>
où T
serait le type de données que vous souhaitez transmettre à la Work
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 ;-)
private static void MyMethod<T>(T myData) { T message = myData; Console.WriteLine($"the thread wrote: {message}"); }
message.Length
n'est pas possible et ainsi de suite)
if(myData.GetType() == typeof(string)) { var str = ((string)(object)myData).Length; }
. Quoi qu'il en soit, au lieu d'utiliser votre méthode de filetage, j'ai trouvé un peu plus confortable à utiliser Tasks<T>
, comme par exemple tasks.Add(Task.Run(() => Calculate(par1, par2, par3)))
, voir ma réponse ci-dessous ( stackoverflow.com/a/59777250/7586301 )
J'avais un problème dans le paramètre passé. J'ai passé un entier d'une boucle for à la fonction et je l'ai affiché, mais cela donnait toujours des résultats différents. comme (1,2,2,3) (1,2,3,3) (1,1,2,3) etc. avec le délégué ParametrizedThreadStart .
ce code simple fonctionnait comme un charme
Thread thread = new Thread(Work);
thread.Start(Parameter);
private void Work(object param)
{
string Parameter = (string)param;
}
Le ParameterizedThreadStart
prend un paramètre. Vous pouvez l'utiliser pour envoyer un paramètre ou une classe personnalisée contenant plusieurs propriétés.
Une autre méthode consiste à placer la méthode que vous souhaitez démarrer en tant que membre d'instance dans une classe avec les propriétés des paramètres que vous souhaitez définir. Créez une instance de la classe, définissez les propriétés et démarrez le thread en spécifiant l'instance et la méthode, et la méthode peut accéder aux propriétés.
Vous pouvez utiliser un délégué ParametrizedThreadStart :
string parameter = "Hello world!";
Thread t = new Thread(new ParameterizedThreadStart(MyMethod));
t.Start(parameter);
Vous pouvez utiliser la méthode BackgroundWorker RunWorkerAsync et transmettre votre valeur.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ConsoleApp6
{
class Program
{
static void Main(string[] args)
{
int x = 10;
Thread t1 =new Thread(new ParameterizedThreadStart(order1));
t1.IsBackground = true;//i can stope
t1.Start(x);
Thread t2=new Thread(order2);
t2.Priority = ThreadPriority.Highest;
t2.Start();
Console.ReadKey();
}//Main
static void order1(object args)
{
int x = (int)args;
for (int i = 0; i < x; i++)
{
Console.ForegroundColor = ConsoleColor.Green;
Console.Write(i.ToString() + " ");
}
}
static void order2()
{
for (int i = 100; i > 0; i--)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.Write(i.ToString() + " ");
}
}`enter code here`
}
}
Je propose d'utiliser Task<T>
au lieu de Thread
; il permet plusieurs paramètres et s'exécute très bien.
Voici un exemple de travail:
public static void Main()
{
List<Task> tasks = new List<Task>();
Console.WriteLine("Awaiting threads to finished...");
string par1 = "foo";
string par2 = "boo";
int par3 = 3;
for (int i = 0; i < 1000; i++)
{
tasks.Add(Task.Run(() => Calculate(par1, par2, par3)));
}
Task.WaitAll(tasks.ToArray());
Console.WriteLine("All threads finished!");
}
static bool Calculate1(string par1, string par2, int par3)
{
lock(_locker)
{
//...
return true;
}
}
// if need to lock, use this:
private static Object _locker = new Object();"
static bool Calculate2(string par1, string par2, int par3)
{
lock(_locker)
{
//...
return true;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ConsoleApp6
{
class Program
{
static void Main(string[] args)
{
int x = 10;
Thread t1 =new Thread(new ParameterizedThreadStart(order1));
t1.Start(x);
Thread t2=new Thread(order2);
t2.Priority = ThreadPriority.Highest;
t2.Start();
Console.ReadKey();
}//Main
static void order1(object args)
{
int x = (int)args;
for (int i = 0; i < x; i++)
{
Console.ForegroundColor = ConsoleColor.Green;
Console.Write(i.ToString() + " ");
}
}
static void order2()
{
for (int i = 100; i > 0; i--)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.Write(i.ToString() + " ");
}
}
}
}