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.