スレッドのThreadStartメソッドにパラメーターを渡す方法は?


291

Thread.ThreadStart()C#でメソッドにパラメーターを渡す方法は?

'download'というメソッドがあるとします

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

これで、メインメソッドに1つのスレッドが作成されました。

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

エラーメソッドタイプが必要です。

パラメータThreadStart付きのターゲットメソッドにパラメータを渡すにはどうすればよいですか?


2
Jon Skeetによって書かれたこの記事をチェックしください。パラメータのセクションは次のページにありますが、記事は全体としてかなり良い読み物です。
codedbadger

回答:


696

最も簡単なのは

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

これの(以上の)利点ParameterizedThreadStartは、複数のパラメーターを渡すことができ、object常にキャストする必要なしにコンパイル時のチェックができることです。


15
オフトピックで申し訳ありませんが、「()」演算子はどういう意味ですか?時々見ますが、チェックする時間がありません。
ŁukaszW.pl

24
引数のないラムダ式です。
Noldorin 2010

31
@ŁukaszW.pl-Noldorinの発言; p C#2.0での代替構成(この例の場合)はnew Thread(delegate() { download(filename); });
Marc Gravell

7
ありません@Tymek かなり正確。キャプチャされた変数は完全な字句閉包として扱われます。これは(実装の詳細として)コンパイラによって生成されたクラスのフィールドとして実装されます。さらに、クロージャスコープは宣言スコープとして定義されます。これは実際には「参照として」ではありません(「参照渡し」と「参照タイプ」はどちらも明確に定義されており、どちらも実際にはこのシナリオを説明していません)
Marc Gravell

5
@MarcGravell-あなたは正しいです。スレッドが開始する前に「ファイル名」が変更された場合は、新しい値が使用されることに注意してください。私はそれのメカニズムについてわざわざ説明するべきではありませんでしたし、参照について話しているべきではありません。
tymtam 2012年

36

この例を見てください:

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

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

最初にワーカーメソッドにデリゲートを渡してスレッドを作成し、次にオブジェクトをパラメーターとして受け取るThread.Startメソッドでスレッドを開始します。

したがって、あなたの場合は次のように使用する必要があります:

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

しかし、 'download'メソッドは、パラメーターとして文字列ではなく、オブジェクトを受け取る必要があります。メソッド本体で文字列にキャストできます。


25

ParameterizedThreadStartパラメーターを受け取るスレッドメソッドにデリゲートを使用する場合。(または実際にはまったくなし、Threadコンストラクタに推測させます。)

使用例:

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

thread.Start(filename)

7

あなたもdelegateそうすることができます...

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


3

スレッド関数(ダウンロード)と必要なパラメーター(ファイル名)をクラスにカプセル化し、ThreadStartデリゲートを使用してスレッド関数を実行できます。

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

私はこのアプローチの方がずっと好きです。ラムダ式のアプローチが常に適切なパラメーターを追跡しているわけではないことがわかりました
平均バニー

3

Fileという別のクラスを用意することをお勧めします。

public class File
{
   private string filename;

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

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

スレッド作成コードで、新しいファイルをインスタンス化します。

string filename = "my_file_name";

myFile = new File(filename);

ThreadStart threadDelegate = new ThreadStart(myFile.download);

Thread newThread = new Thread(threadDelegate);

0

これはどうですか(または、このように使用しても問題ありませんか?)

var test = "Hello";
new Thread(new ThreadStart(() =>
{
    try
    {
        //Staff to do
        Console.WriteLine(test);
    }
    catch (Exception ex)
    {
        throw;
    }
})).Start();

-1

あなたの質問によると...

C#でThread.ThreadStart()メソッドにパラメーターを渡す方法は?

...そして発生したエラーは、コードを修正する必要があります

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

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



ただし、問題は最初は思われるほど複雑になります。

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のスレッドの方法。

ただし、このアプローチには大きな問題が1つあり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>TWork

ただし、この問題には推奨される解決策があります。これには、スレッドに渡されるデータと、次のようなワーカーメソッドを表すメソッドの両方を含むクラスの作成が含まれます。

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();

したがって、この方法では、キャストを回避するだけで、スレッドにデータを提供するタイプセーフな方法があります;-)


-2

ここが完璧な方法です...

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);
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.