จะส่งพารามิเตอร์ไปยังวิธีการ ThreadStart ใน Thread ได้อย่างไร


291

วิธีการส่งผ่านพารามิเตอร์ไปยังThread.ThreadStart()วิธีการใน C #?

สมมติว่าฉันมีวิธีการที่เรียกว่า 'ดาวน์โหลด'

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

ตอนนี้ฉันได้สร้างหนึ่งเธรดในวิธีการหลัก:

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

คาดว่าจะเกิดข้อผิดพลาดประเภทวิธี

ฉันจะส่งพารามิเตอร์ไปยังThreadStartด้วยวิธีการเป้าหมายพร้อมพารามิเตอร์ได้อย่างไร


2
ตรวจสอบนี้บทความที่เขียนโดยส่วนจอนสกีตพารามิเตอร์คือในหน้าถัดไป แต่บทความโดยรวมเป็นที่ดีอ่านสวย
codingbadger

คำตอบ:


696

ที่ง่ายที่สุดคือเพียงแค่

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

ข้อได้เปรียบของ (เกินParameterizedThreadStart) นี้คือคุณสามารถผ่านพารามิเตอร์หลายตัวและคุณจะได้รับการตรวจสอบเวลาคอมไพล์โดยไม่จำเป็นต้องแคสต์objectตลอดเวลา


15
ฉันขอโทษสำหรับความไม่พอใจ แต่ตัวดำเนินการ '()' หมายถึงอะไร ฉันเห็นมันบางครั้ง แต่ฉันไม่มีเวลาตรวจสอบ
ŁukaszW.pl

24
มันเป็นแลมบ์ดาที่ไม่มีข้อโต้แย้ง
Noldorin

31
@ ŁukaszW.pl - สิ่งที่ Noldorin กล่าว; p ใน C # 2.0 โครงสร้างทางเลือก (สำหรับตัวอย่างนี้) คือnew Thread(delegate() { download(filename); });
Marc Gravell

7
@Tymek ที่ไม่ค่อยแม่นยำ ตัวแปรใด ๆ ที่จับจะถือว่าเป็นการปิดคำศัพท์เต็มรูปแบบซึ่ง (ตามรายละเอียดการนำไปใช้) จะถูกนำไปใช้เป็นฟิลด์ในคลาสที่คอมไพเลอร์สร้างขึ้น นอกจากนี้ขอบเขตการปิดถูกกำหนดให้เป็นขอบเขตการประกาศ ไม่ใช่ "เป็นข้อมูลอ้างอิง" เช่นนี้ ("การอ้างอิงผ่าน" และ "ประเภทการอ้างอิง" มีการกำหนดไว้อย่างดีและไม่ได้อธิบายสถานการณ์นี้จริง ๆ )
Marc Gravell

5
@MarcGravell - คุณถูกต้อง ทั้งหมดที่ฉันควรจะกล่าวคือหนึ่งควรทราบว่าหากการเปลี่ยนแปลง 'ชื่อไฟล์' ก่อนที่ด้ายจะเริ่มค่าใหม่จะถูกใช้ ฉันไม่ควรพูดพล่ามเกี่ยวกับกลไกของมันและฉันไม่ควรพูดถึงการอ้างอิง
tymtam

37

ดูตัวอย่างนี้:

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

แต่วิธีการ 'ดาวน์โหลด' ของคุณยังคงต้องใช้วัตถุไม่ใช่สตริงเป็นพารามิเตอร์ คุณสามารถส่งไปที่สตริงในร่างกายวิธีการของคุณ


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

ฉันอยากจะแนะนำให้คุณมีคลาสอื่นที่เรียกว่าไฟล์

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

ตามคำถามของคุณ ...

วิธีการส่งผ่านพารามิเตอร์ไปที่ Thread.ThreadStart () วิธีการใน C #?

... และข้อผิดพลาดที่คุณพบคุณจะต้องแก้ไขรหัสของคุณจาก

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เมธอดของเธรด

อย่างไรก็ตามมีปัญหาใหญ่อย่างหนึ่งในวิธีการนี้: ทุกสิ่งที่ผ่านเข้าสู่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();

ดังนั้นด้วยวิธีนี้คุณหลีกเลี่ยงการแคสต์และมีวิธีที่ปลอดภัยในการให้ข้อมูลกับเธรด ;-)


-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.