ThreadStart z parametrami


261

Jak rozpocząć wątek z parametrami w C #?


Odpowiedź na to pytanie różni się znacznie w zależności od wersji środowiska wykonawczego - czy odpowiedź 3.5 jest w porządku?
quillbreaker

4
Łał. Edytowałem niektóre z twoich starych pytań, ale może to być praca na pełny etat. Zapomniałem, jak bardzo się poprawiłeś na przestrzeni lat. :-)
John Saunders,

Gdybym zadał tak krótkie pytanie, otrzymałbym 5 negatywnych punktów lub nawet więcej! Chociaż pytanie i odpowiedź pomogły mi.
Mohammad Musavi

Odpowiedzi:


174

Tak :

Thread t = new Thread (new ParameterizedThreadStart(myMethod));
t.Start (myParameterObject);

14
czy to to samo: ThreadStart processTaskThread = delegate {ProcessTasks (databox.DataboxID); }; nowy wątek (processTaskThread) .Start ();
JL.

43
Co to jest myParamObject i myUrl?
dialex

3
W takim przypadku void MyParamObject(object myUrl){ //do stuff }powinien mieć typ parametruobject
Elshan

15
-1, ponieważ odpowiedź zakłada, że ​​PO wie, jak korzystać ParameterizedThreadStarti wyraźnie z tekstu pytania, prawdopodobnie tak nie jest.
JYelton,

2
Mam ten błąd Błąd CS0123 Brak przeciążenia dla „UpdateDB” odpowiada delegatowi „ParameterizedThreadStart”
Omid Farvid

482

Jedno z 2 przeciążeń konstruktora wątków pobiera delegata ParameterizedThreadStart, który pozwala przekazać pojedynczy parametr do metody startowej. Niestety dopuszcza tylko jeden parametr i robi to w niebezpieczny sposób, ponieważ przekazuje go jako obiekt. Uważam, że o wiele łatwiej jest użyć wyrażenia lambda, aby uchwycić odpowiednie parametry i przekazać je w mocno typowy sposób.

Spróbuj wykonać następujące czynności

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

41
+1: Mimo że aktualnie wybrana odpowiedź jest absolutnie poprawna, ta autorstwa JaredPara jest lepsza. Jest to po prostu najlepsze rozwiązanie dla większości praktycznych przypadków.
galaktor

2
To rozwiązanie jest znacznie lepsze niż standardowy ParameterizedThreadStart
Piotr Owsiak

5
Miło takie proste. Po prostu zawiń dowolne wywołanie w „new Thread (() => FooBar ()) .Start ();
Thomas Jespersen

12
Niesamowite, to jest dla facetów VB.NETDim thr As New Thread(Sub() DoStuff(settings))
dr. zło

3
@bazaza Miałem właśnie na myśli sprawdzanie typu statycznego
JaredPar

141

Możesz użyć wyrażeń lambda

private void MyMethod(string param1,int param2)
{
  //do stuff
}
Thread myNewThread = new Thread(() => MyMethod("param1",5));
myNewThread.Start();

to jak dotąd najlepsza odpowiedź, jaką mogłem znaleźć, jest szybki i łatwy.


6
Najlepsze rozwiązanie dla prostych przypadków IMO
Dunc

1
co to jest =>? i gdzie mogę znaleźć więcej informacji na temat składni?
Nick

2
To wyrażenie lambda, niektóre informacje można znaleźć pod tymi adresami: msdn.microsoft.com/en-us/library/vstudio/bb397687.aspx | codeproject.com/Articles/24255/Exploring-Lambda-Expression-in-C | dotnetperls.com/lambda
Georgi-it

1
To zadziałało dla mnie. Próbowałem ParameterizedThreadStart i jego odmian, ale nie miałem radości. Korzystałem z .NET Framework 4 w podobno prostej aplikacji konsolowej.
Daniel Hollinrake

Działa to najlepiej dla osób przyzwyczajonych do tego rodzaju delegatów. Początkujący mogą mieć trudności z udnerstand. Jest to jednak czyste jak na standardy C #. Przyjęta odpowiedź nie działa dla mnie i nie mam czasu, aby dowiedzieć się, dlaczego.
Bitterblue,

37
Thread thread = new Thread(Work);
thread.Start(Parameter);

private void Work(object param)
{
    string Parameter = (string)param;
}

Typ parametru musi być obiektem.

EDYTOWAĆ:

Chociaż ta odpowiedź nie jest niepoprawna, odradzam takie podejście. Korzystanie z wyrażenia lambda jest znacznie łatwiejsze do odczytania i nie wymaga rzutowania tekstu. Zobacz tutaj: https://stackoverflow.com/a/1195915/52551


Dlaczego pomagasz z kodem, który się nie kompiluje;) Parameter?
Sebastian Xawery Wiśniowiecki

32
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"
    }
}

3
To daje mi „Żadne przeciążenie dla„ DoWork ”pasuje do delegowanego„ System.Threading.ParameterizedThreadStart ”
anon58192932

1
Jaka byłaby różnica, gdybyś właśnie przekazał ThreadMethod podczas inicjalizacji wątku?
Joe

Pamiętaj, że typ parametru musi być typu „Obiekt”
Kunal Uppal

28

Prosty sposób z wykorzystaniem lambda tak…

Thread t = new Thread(() => DoSomething("param1", "param2"));
t.Start();

LUB możesz nawet delegateużyć ThreadStarttak ...

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

LUB przy użyciu VS 2019 .NET 4.5+ jeszcze czystszy tak ..

private void DoSomething(int param1, string param2)
{
    //DO SOMETHING..
    void ts()
    {
        if (param1 > 0) DoSomethingElse(param2, "param3");
    }
    new Thread(ts).Start();
    //DO SOMETHING..
}



6

Jak już wspomniano w różnych odpowiedziach tutaj, Threadklasa obecnie (4.7.2) zapewnia kilka konstruktorów i Startmetodę z przeciążeniami.

Tymi odpowiednimi konstruktorami tego pytania są:

public Thread(ThreadStart start);

i

public Thread(ParameterizedThreadStart start);

które biorą ThreadStartdelegata lub ParameterizedThreadStartdelegata.

Odpowiedni delegaci wyglądają tak:

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

Jak można zauważyć, poprawnym konstruktorem do użycia wydaje się być ten, który bierze ParameterizedThreadStartdelegata, aby pewna metoda zgodna z określoną sygnaturą delegata mogła zostać uruchomiona przez wątek.

Prostym przykładem Threadbyłoby zainicjowanie klasy

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

Lub tylko

Thread thread = new Thread(Work);

Podpis odpowiedniej metody (wywołanej Workw tym przykładzie) wygląda następująco:

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

Pozostało rozpocząć wątek. Odbywa się to za pomocą jednego z nich

public void Start();

lub

public void Start(object parameter);

Chociaż Start()uruchamia wątek i przekazuje nulldane do metody, Start(...)może służyć do przekazywania wszystkiego do Workmetody wątku.

Jest jednak jeden duży problem z tym podejściem: wszystko przekazane do Workmetody jest rzutowane na obiekt. Oznacza to, że w ramach Workmetody należy ponownie rzutować na oryginalny typ, jak w poniższym przykładzie:

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



Przesyłanie jest czymś, czego zwykle nie chcesz robić.

Co się stanie, jeśli ktoś przekaże coś innego, co nie jest łańcuchem? Ponieważ na początku wydaje się to niemożliwe (ponieważ jest to moja metoda, wiem, co robię lub metoda ta jest prywatna, w jaki sposób ktoś powinien być w stanie przekazać coś do niej? ), Być może z tego powodu możesz skończyć dokładnie z tą sprawą . Ponieważ niektóre przypadki mogą nie stanowić problemu, inne tak. W takich przypadkach prawdopodobnie skończysz z InvalidCastExceptionczymś, czego prawdopodobnie nie zauważysz, ponieważ po prostu kończy wątek.

Jako rozwiązanie można oczekiwać ogólnego ParameterizedThreadStartdelegata, na przykład, ParameterizedThreadStart<T>gdzie Tbyłby typ danych, które chcesz przekazać do Workmetody. Niestety coś takiego jeszcze nie istnieje (jeszcze?).

Istnieje jednak sugerowane rozwiązanie tego problemu. Polega ona na utworzeniu klasy, która zawiera zarówno dane, które mają być przekazywane do wątku, jak i metodę reprezentującą metodę roboczą w następujący sposób:

public class ThreadWithState
{
    private string message;

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

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

Przy takim podejściu zaczynasz wątek w następujący sposób:

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

thread.Start();

W ten sposób po prostu unikniesz przerzucania się i masz bezpieczny sposób dostarczania danych do wątku ;-)


Wow, głosowanie bez komentarza ... Albo moja odpowiedź jest tak samo zła, jak obsada, albo czytelnik nie zrozumiał, co próbowałem tu wskazać ;-)
Markus Safar

1
Uważam twoje rozwiązanie za bardzo oświecające, gratulacje. Chciałem tylko dodać, że testowałem już w Net.Core poniżej i działałem bez konieczności rzucania eksplozji! :-) private static void MyMethod<T>(T myData) { T message = myData; Console.WriteLine($"the thread wrote: {message}"); }
Paul Efford

@PaulEfford Dzięki ;-) Twoje rozwiązanie wydaje się całkiem fajne. Ale nie masz dostępu do wpisywania określonych informacji, ponieważ nadal będą one zapakowane w obiekt, prawda? (np. message.Lengthnie jest możliwe itd.)
Markus Safar

1
racja ... możesz message.GetType () i rzutować, jeśli wymagane są jakieś określone właściwości, takie jak if(myData.GetType() == typeof(string)) { var str = ((string)(object)myData).Length; }. W każdym razie zamiast korzystania z metody wątkowania uznałem, że jest to bardziej wygodne w użyciu Tasks<T>, na przykład tasks.Add(Task.Run(() => Calculate(par1, par2, par3)))zobacz moją odpowiedź poniżej ( stackoverflow.com/a/59777250/7586301 )
Paul Efford

5

Miałem problem z przekazanym parametrem. Przekazałem do funkcji liczbę całkowitą z pętli for i wyświetliłem ją, ale zawsze dawała inne wyniki. jak (1,2,2,3) (1,2,3,3) (1,1,2,3) itd. z delegatem ParametrizedThreadStart .

ten prosty kod działał jako urok

Thread thread = new Thread(Work);
thread.Start(Parameter);

private void Work(object param) 
{
 string Parameter = (string)param; 
}

4

ParameterizedThreadStartPobiera jeden parametr. Możesz użyć tego do wysłania jednego parametru lub niestandardowej klasy zawierającej kilka właściwości.

Inną metodą jest umieszczenie metody, którą chcesz uruchomić jako element instancji w klasie, wraz z właściwościami parametrów, które chcesz ustawić. Utwórz instancję klasy, ustaw właściwości i uruchom wątek, określając instancję i metodę, a metoda może uzyskać dostęp do właściwości.


3

Możesz użyć delegata ParametrizedThreadStart :

string parameter = "Hello world!";
Thread t = new Thread(new ParameterizedThreadStart(MyMethod));
t.Start(parameter);


1
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`
    }
}

0

Proponuję używać Task<T>zamiast Thread; pozwala na wiele parametrów i działa naprawdę dobrze.

Oto działający przykład:

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

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

Wielowątkowość z wątkami C # umożliwia tworzenie bardziej wydajnych aplikacji synchronizujących za pośrednictwem pamięci współużytkowanej.
Mohammed Hassen Ismaile
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.