Como passar parâmetros para o método ThreadStart no Thread?

291

Como passar parâmetros para o Thread.ThreadStart()método em c #?

Suponha que eu tenha um método chamado 'download'

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

Agora eu criei um thread no método principal:

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

tipo de método de erro esperado.

Como posso passar parâmetros para ThreadStartcom o método target com parâmetros?

Swapnil Gupta
fonte
2
Confira este artigo escrito por Jon Skeet A seção Parâmetros está na próxima página, mas o artigo como um todo é uma boa leitura.
Codingbadger

Respostas:

696

O mais simples é apenas

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

A (s) vantagem (s) disso (acima ParameterizedThreadStart) é que você pode passar vários parâmetros e obter a verificação do tempo de compilação sem precisar converter objecto tempo todo.

Marc Gravell
fonte
15
Sinto muito pelo offtopic, mas o que significa o operador '()'? Às vezes, vejo isso, mas não tenho tempo para verificar.
ŁukaszW.pl 29/07
24
É uma expressão lambda sem argumentos.
Noldorin
31
@ ŁukaszW.pl - o que Noldorin disse; p em C # 2.0, uma construção alternativa (para este exemplo) énew Thread(delegate() { download(filename); });
Marc Gravell
7
@ Tymek que não é muito preciso; quaisquer variáveis ​​capturadas são tratadas como fechamentos lexicais completos , que (como um detalhe de implementação) são implementados como campos em uma classe gerada pelo compilador. Além disso, o escopo de fechamento é definido como o escopo da declaração. Não é realmente "como referências" como tal ( "passar por referência" e "tipos de referência" são bem definidos, e nem realmente descreve este cenário)
Marc Gravell
5
@ MarcGravell - você está correto. Tudo o que eu deveria ter dito é que é preciso estar ciente de que, se o 'nome do arquivo' mudar antes do início do encadeamento, o novo valor será usado. Eu não deveria estar tagarelando sobre a mecânica disso e definitivamente não deveria estar falando sobre referências.
tymtam
36

Veja este exemplo:

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

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

Primeiro, você cria um encadeamento passando o método delegate to worker e o inicia com um método Thread.Start que leva seu objeto como parâmetro.

Portanto, no seu caso, você deve usá-lo assim:

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

Mas o seu método 'download' ainda precisa pegar um objeto , não uma string como parâmetro. Você pode convertê-lo em string no corpo do método.

ŁukaszW.pl
fonte
25

Você deseja usar o ParameterizedThreadStartdelegado para métodos de encadeamento que usam parâmetros. (Ou nenhum, na verdade, e deixe o Threadconstrutor inferir.)

Exemplo de uso:

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

thread.Start(filename)
Noldorin
fonte
7

Você também pode delegategostar ...

ThreadStart ts = delegate
{
      bool moreWork = DoWork("param1", "param2", "param3");
      if (moreWork) 
      {
          DoMoreWork("param1", "param2");
      }
};
new Thread(ts).Start();
Mestre Mick
fonte
4

Em Adicional

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

Você pode encapsular a função de encadeamento (download) e os parâmetros necessários (nome do arquivo) em uma classe e usar o delegado ThreadStart para executar a função de encadeamento.

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
fonte
Eu gosto desta abordagem muito melhor, descobri que a abordagem expressão lambda não é sempre manter o controle dos parâmetros corretos
meanbunny
3

Eu recomendo que você tenha outra classe chamada Arquivo.

public class File
{
   private string filename;

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

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

E no seu código de criação de threads, você instancia um novo arquivo:

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
fonte
0

Que tal isso: (ou está ok para usar assim?)

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

De acordo com sua pergunta ...

Como passar parâmetros para o método Thread.ThreadStart () em C #?

... e o erro encontrado, você precisaria corrigir seu código de

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

para

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



No entanto, a questão é mais complexa, como parece à primeira vista.

A Threadclasse atualmente (4.7.2) fornece vários construtores e um Startmétodo com sobrecargas.

Esses construtores relevantes para esta pergunta são:

public Thread(ThreadStart start);

e

public Thread(ParameterizedThreadStart start);

que leva um ThreadStartdelegado ou um ParameterizedThreadStartdelegado.

Os delegados correspondentes ficam assim:

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

Portanto, como pode ser visto, o construtor correto a ser usado parece ser aquele que recebe um ParameterizedThreadStartdelegado, para que algum método em conformidade com a assinatura especificada do delegado possa ser iniciado pelo encadeamento.

Um exemplo simples para instanciar a Threadclasse seria

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

ou apenas

Thread thread = new Thread(Work);

A assinatura do método correspondente (chamada Workneste exemplo) se parece com isso:

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

O que resta é iniciar o thread. Isso é feito usando qualquer um

public void Start();

ou

public void Start(object parameter);

Enquanto Start()iniciaria o encadeamento e passaria nullcomo dados para o método, Start(...)pode ser usado para passar qualquer coisa para o Workmétodo do encadeamento.

No entanto, há um grande problema com essa abordagem: tudo o Workque é passado no método é convertido em um objeto. Isso significa que, dentro do Workmétodo, ele deve ser convertido para o tipo original novamente, como no exemplo a seguir:

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



A transmissão é algo que você normalmente não deseja fazer.

E se alguém passa outra coisa que não é uma string? Como isso parece inicialmente impossível (porque é o meu método, eu sei o que faço ou O método é privado, como alguém deve ser capaz de transmitir alguma coisa a ele? ), Você pode acabar exatamente com esse caso por vários motivos . Como alguns casos podem não ser um problema, outros são. Nesses casos, você provavelmente terminará com um InvalidCastExceptionque provavelmente não notará porque simplesmente encerra o encadeamento.

Como solução, você esperaria obter um ParameterizedThreadStartrepresentante genérico como ParameterizedThreadStart<T>onde Tseria o tipo de dados que você deseja passar para o Workmétodo. Infelizmente, algo assim não existe (ainda?).

Existe, no entanto, uma solução sugerida para esse problema. Envolve a criação de uma classe que contém os dados a serem passados ​​para o encadeamento, bem como o método que representa o método worker como este:

public class ThreadWithState
{
    private string message;

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

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

Com essa abordagem, você iniciaria o thread assim:

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

thread.Start();

Dessa forma, você simplesmente evita a transmissão e tem uma maneira tipicamente segura de fornecer dados para um thread ;-)

Markus Safar
fonte
-2

aqui é o caminho perfeito ...

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
fonte