ThreadStart с параметрами


261

Как начать поток с параметрами в C #?


Ответ на этот вопрос широко варьируется в зависимости от версии среды выполнения - хорошо ли ответ 3.5?
Quillbreaker

4
Вот это да. Я редактировал некоторые твои старые вопросы, но это могла быть работа на полный рабочий день. Я забыл, насколько ты улучшился за эти годы. :-)
Джон Сондерс

Если бы я задал такой краткий вопрос, я бы получил 5 отрицательных оценок или даже больше! Хотя вопрос и ответ мне помогли.
Мухаммед Мусави

Ответы:


174

Ага :

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

14
это то же самое: ThreadStart processTaskThread = делегат {ProcessTasks (databox.DataboxID); }; новый поток (processTaskThread) .Start ();
JL.

43
Что такое myParamObject и myUrl?
диалекс

3
В этом случае void MyParamObject(object myUrl){ //do stuff }должен иметь тип параметраobject
Эльшан

15
-1 потому что ответ предполагает, что ОП знает, как использовать ParameterizedThreadStartи ясно из текста вопроса, это, вероятно, не так.
Джелтон

2
У меня есть эта ошибка Ошибка CS0123 Нет перегрузки для 'UpdateDB' соответствует делегату 'ParameterizedThreadStart'
Омид Фарвид

482

Одна из двух перегрузок конструктора Thread принимает делегат ParameterizedThreadStart, который позволяет передать единственный параметр методу start. К сожалению, он допускает только один параметр и делает это небезопасным способом, поскольку передает его как объект. Я считаю, что гораздо проще использовать лямбда-выражение для захвата соответствующих параметров и передачи их строго типизированным способом.

Попробуйте следующее

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: Несмотря на то, что выбранный в настоящее время ответ является абсолютно правильным, этот ответ от JaredPar является лучшим. Это просто лучшее решение для большинства практических случаев.
Галактор

2
Это решение намного лучше, чем стандартный ParameterizedThreadStart
Петр Овсяк

5
Хорошо, так просто. Просто оберните любой вызов в "new Thread (() => FooBar ()) .Start ();
Томас Йесперсен

12
Круто, это для ребят из VB.NETDim thr As New Thread(Sub() DoStuff(settings))
доктор. зло

3
@bavaza Я только что имел в виду статическую проверку типов
JaredPar

141

Вы можете использовать лямбда-выражения

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

это пока лучший ответ, который я мог найти, это быстро и легко.


6
Лучшее решение для простых случаев ИМО
Дунк

1
что это =>? и где я могу найти больше информации о синтаксисе?
Ник

2
Это лямбда-выражение, некоторую информацию можно найти по следующим адресам: msdn.microsoft.com/en-us/library/vstudio/bb397687.aspx | codeproject.com/Articles/24255/Exploring-Lambda-Expression-in-C | dotnetperls.com/lambda
Georgi-it

1
Это сработало для меня. Я попробовал ParameterizedThreadStart и его варианты, но не испытал радости. Я использовал .NET Framework 4 в предположительно простом консольном приложении.
Даниэль Холлинрейк

Это работает лучше всего для людей, которые привыкли к такого рода делегатам. Новичкам может быть трудно разобраться. Это чисто для стандартов C #. Принятый ответ не работает для меня, и у меня нет времени, чтобы выяснить, почему.
Bitterblue

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

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

Тип параметра должен быть объектом.

РЕДАКТИРОВАТЬ:

Хотя этот ответ не является правильным, я рекомендую против такого подхода. Использование лямбда-выражения намного проще для чтения и не требует приведения типов. Смотрите здесь: https://stackoverflow.com/a/1195915/52551


Почему вы помогаете с кодом, который не компилируется;) Parameter?
Себастьян Ксавери Вишневецкий

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
Это дает мне «Нет перегрузки для делегата совпадений DoWork» System.Threading.ParameterizedThreadStart '
anon58192932

1
Какая разница, если вы только что передали ThreadMethod в инициализацию Thread t?
Джо

Помните, что тип параметра должен быть типа «Объект»
Кунал Уппал

28

Простой способ использования лямбда, как это ..

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

ИЛИ вы могли бы даже delegateиспользовать, ThreadStartкак так ...

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

ИЛИ используя VS 2019 .NET 4.5+ даже чище, как это ..

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



6

Как уже упоминалось в различных ответах здесь, Threadкласс в настоящее время (4.7.2) предоставляет несколько конструкторов и Startметод с перегрузками.

Эти соответствующие конструкторы для этого вопроса:

public Thread(ThreadStart start);

и

public Thread(ParameterizedThreadStart start);

которые либо принимают ThreadStartделегата, либо ParameterizedThreadStartделегата.

Соответствующие делегаты выглядят так:

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

Таким образом, как видно, правильный конструктор, который нужно использовать, - это тот, который принимает ParameterizedThreadStartделегат, так что поток, соответствующий некоторому методу, соответствующему указанной сигнатуре делегата, может быть запущен потоком.

Простой пример создания экземпляра Threadкласса:

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

или просто

Thread thread = new Thread(Work);

Сигнатура соответствующего метода (вызываемого Workв этом примере) выглядит следующим образом:

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

Осталось только начать тему. Это делается с помощью либо

public void Start();

или

public void Start(object parameter);

Хотя Start()бы запустить поток и передать в nullкачестве данных в метод, Start(...)может использоваться для передачи чего-либо в Workметод потока.

Однако у этого подхода есть одна большая проблема: все, что передается в Workметод, преобразуется в объект. Это означает, что в Workметоде он должен быть снова приведен к исходному типу, как в следующем примере:

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



Кастинг - это то, что вы обычно не хотите делать.

Что если кто-то пропустит что-то еще, что не является строкой? Поскольку вначале это кажется невозможным (поскольку это мой метод, я знаю, что я делаю, или метод частный, как кто-то может быть в состоянии что-либо передать ему? ), Вы можете в конечном итоге получить именно этот случай по разным причинам. , Поскольку некоторые случаи могут не быть проблемой, другие - нет. В таких случаях вы, вероятно, в конечном итоге InvalidCastExceptionполучите, который вы, вероятно, не заметите, потому что он просто завершает поток.

В качестве решения вы ожидаете получить общий ParameterizedThreadStartделегат, например, ParameterizedThreadStart<T>где Tбудет тип данных, которые вы хотите передать в Workметод. К сожалению что-то подобное не существует (пока?).

Однако есть предложенное решение этой проблемы. Он включает в себя создание класса, который содержит как данные, передаваемые в поток, так и метод, представляющий метод работ, например:

public class ThreadWithState
{
    private string message;

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

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

При таком подходе вы начинаете поток так:

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

thread.Start();

Таким образом, вы просто избегаете перебора и имеете безопасный способ предоставления данных потоку ;-)


Ничего себе, отрицательное голосование без комментариев ... Либо мой ответ так же плох, как у актеров, либо читатель не понял, на что я здесь пытался указать ;-)
Маркус Сафар

1
Я нашел ваше решение очень enlightnen, поздравляю. Просто хотел добавить, что я уже тестировал в Net.Core следующее и работал без необходимости явного приведения! :-) private static void MyMethod<T>(T myData) { T message = myData; Console.WriteLine($"the thread wrote: {message}"); }
Пол Эффорд

@PaulEfford Спасибо ;-) Ваше решение кажется довольно хорошим. Но вы не получаете доступ к информации, относящейся к типу, поскольку она все еще будет помещена в объект, верно? (например, message.Lengthневозможно и так далее)
Маркус Сафар

1
правильно ... вы могли бы message.GetType () и привести, если требуется любое конкретное свойство, как if(myData.GetType() == typeof(string)) { var str = ((string)(object)myData).Length; }. В любом случае, вместо того, чтобы использовать ваш метод многопоточности, я нашел его более удобным Tasks<T>, как, например tasks.Add(Task.Run(() => Calculate(par1, par2, par3))), см. Мой ответ ниже ( stackoverflow.com/a/59777250/7586301 )
Пол Эффорд

5

У меня была проблема в переданном параметре. Я передал целое число из цикла for в функцию и отобразил ее, но она всегда давала разные результаты. как (1,2,2,3) (1,2,3,3) (1,1,2,3) и т. д. с делегатом ParametrizedThreadStart .

этот простой код работал как шарм

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

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

4

ParameterizedThreadStartПринимает один параметр. Вы можете использовать это для отправки одного параметра или пользовательского класса, содержащего несколько свойств.

Другой метод - поместить метод, который вы хотите запустить как член экземпляра, в класс вместе со свойствами для параметров, которые вы хотите установить. Создайте экземпляр класса, установите свойства и запустите поток, указав экземпляр и метод, и метод может получить доступ к свойствам.


3

Вы можете использовать делегат 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

Я предлагаю использовать Task<T>вместо Thread; он допускает несколько параметров и работает очень хорошо.

Вот рабочий пример:

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

Многопоточность с потоками C # позволяет разрабатывать более эффективные приложения, синхронизирующиеся через общую память.
Мохаммед Хассен Исмаил
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.