Làm thế nào để truyền tham số cho phương thức ThreadStart trong Thread?


291

Làm thế nào để truyền tham số cho Thread.ThreadStart() phương thức trong C #?

Giả sử tôi có phương pháp gọi là 'tải xuống'

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

Bây giờ tôi đã tạo một chủ đề trong phương thức chính:

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

loại phương pháp lỗi dự kiến.

Làm thế nào tôi có thể truyền tham số cho ThreadStartphương thức đích với tham số?


2
Kiểm tra này bài viết được viết bởi phần Jon Skeet Các thông số là trên trang tiếp theo nhưng bài viết như một toàn thể là một đọc tốt đẹp.
codbadger

Câu trả lời:


696

Đơn giản nhất là

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

Ưu điểm của (hơn ParameterizedThreadStart) này là bạn có thể truyền nhiều tham số và bạn có thể kiểm tra thời gian biên dịch mà không cần phải truyền từ objectmọi lúc.


15
Tôi xin lỗi vì ngoại lệ nhưng toán tử '()' nghĩa là gì? Đôi khi tôi thấy nó nhưng tôi không có thời gian để kiểm tra.
ŁukaszW.pl

24
Đó là một biểu thức lambda không có đối số.
Noldorin

31
@ UkaszW.pl - những gì Noldorin đã nói; p trong C # 2.0 một cấu trúc thay thế (ví dụ này) lànew Thread(delegate() { download(filename); });
Marc Gravell

7
@Tymek điều đó không hoàn toàn chính xác; bất kỳ biến nào được bắt giữ đều được coi là các bao đóng từ vựng đầy đủ , mà (như một chi tiết triển khai) được thực hiện như các trường trên một lớp do trình biên dịch tạo. Ngoài ra, phạm vi đóng được xác định là phạm vi khai báo. Nó không thực sự "như tài liệu tham khảo" như vậy ("vượt qua tham chiếu" và "loại tham chiếu" đều được xác định rõ và cũng không thực sự mô tả kịch bản này)
Marc Gravell

5
@MarcGravell - bạn đã đúng. Tất cả những gì tôi nên nói là người ta nên biết rằng nếu 'tên tệp' thay đổi trước khi luồng bắt đầu, giá trị mới sẽ được sử dụng. Tôi không nên nói quá nhiều về cơ chế của nó và tôi chắc chắn không nên nói về việc tham khảo.
tymtam

36

Nhìn vào ví dụ này:

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

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

Trước tiên, bạn tạo một luồng bằng cách chuyển ủy nhiệm cho phương thức worker và sau đó khởi động nó bằng phương thức Thread.Start lấy đối tượng của bạn làm tham số.

Vì vậy, trong trường hợp của bạn, bạn nên sử dụng nó như thế này:

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

Nhưng phương thức 'tải xuống' của bạn vẫn cần lấy đối tượng , không phải chuỗi như một tham số. Bạn có thể truyền nó thành chuỗi trong cơ thể phương thức của bạn.


25

Bạn muốn sử dụng ParameterizedThreadStartủy nhiệm cho các phương thức luồng lấy tham số. (Hoặc không có gì cả thực sự, và hãy đểThread xây dựng suy luận.)

Ví dụ sử dụng:

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

thread.Start(filename)

7

Bạn cũng có thể delegatethích như vậy ...

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

4

Bổ sung

    Thread thread = new Thread(delegate() { download(i); });
    thread.Start();

3

Bạn có thể đóng gói chức năng luồng (tải xuống) và (các) tham số cần thiết (tên tệp) trong một lớp và sử dụng ủy quyền ThreadStart để thực thi chức năng luồng.

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

Tôi thích cách tiếp cận này tốt hơn nhiều, tôi thấy rằng cách tiếp cận biểu thức lambda không phải lúc nào cũng theo dõi các tham số đúng
meanbunny

3

Tôi muốn giới thiệu bạn có một lớp khác gọi là File.

public class File
{
   private string filename;

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

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

Và trong mã tạo chủ đề của bạn, bạn khởi tạo một tệp mới:

string filename = "my_file_name";

myFile = new File(filename);

ThreadStart threadDelegate = new ThreadStart(myFile.download);

Thread newThread = new Thread(threadDelegate);

0

Làm thế nào về điều này: (hoặc nó là ok để sử dụng như thế này?)

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

-1

Theo câu hỏi của bạn ...

Làm cách nào để truyền tham số cho phương thức Thread.ThreadStart () trong C #?

... và lỗi bạn gặp phải, bạn sẽ phải sửa mã của mình từ

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

đến

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



Tuy nhiên, câu hỏi phức tạp hơn lúc đầu.

Các Threadlớp học hiện nay (4.7.2) cung cấp một số nhà xây dựng và một Startphương pháp với quá tải.

Những nhà xây dựng có liên quan cho câu hỏi này là:

public Thread(ThreadStart start);

public Thread(ParameterizedThreadStart start);

trong đó có một ThreadStartđại biểu hoặc một ParameterizedThreadStartđại biểu.

Các đại biểu tương ứng trông như thế này:

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

Vì vậy, như có thể thấy, các nhà xây dựng chính xác để sử dụng dường như là một trong những ParameterizedThreadStart đại biểu để một số phương thức phù hợp với chữ ký được chỉ định của đại biểu có thể được bắt đầu bởi luồng.

Một ví dụ đơn giản để kích hoạt Threadlớp học sẽ là

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

hoặc chỉ

Thread thread = new Thread(Work);

Chữ ký của phương thức tương ứng (được gọi Worktrong ví dụ này) trông như thế này:

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

Những gì còn lại là bắt đầu chuỗi. Điều này được thực hiện bằng cách sử dụng một trong hai

public void Start();

hoặc là

public void Start(object parameter);

Trong khi Start()sẽ bắt đầu luồng và truyền nulldưới dạng dữ liệu cho phương thức, Start(...)có thể được sử dụng để truyền bất cứ thứ gì vào Workphương thức của luồng.

Tuy nhiên, có một vấn đề lớn với cách tiếp cận này: Mọi thứ được truyền vào Workphương thức đều được đưa vào một đối tượng. Điều đó có nghĩa là trong Workphương thức nó phải được chuyển sang loại ban đầu như trong ví dụ sau:

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



Đúc là điều bạn thường không muốn làm.

Điều gì xảy ra nếu ai đó vượt qua một thứ khác không phải là một chuỗi? Vì điều này ban đầu dường như không thể (vì đó là phương pháp của tôi, tôi biết tôi làm gì hoặc Phương pháp này là riêng tư, làm thế nào để ai đó có thể chuyển bất cứ điều gì cho nó? ) Bạn có thể kết thúc chính xác với trường hợp đó vì nhiều lý do . Vì một số trường hợp có thể không phải là một vấn đề, những người khác là. Trong những trường hợp như vậy, bạn có thể sẽ kết thúc với mộtInvalidCastException cái mà bạn có thể sẽ không nhận thấy bởi vì nó chỉ đơn giản là chấm dứt chuỗi.

Là một giải pháp bạn mong đợi để có được một ParameterizedThreadStartđại biểu chung như ParameterizedThreadStart<T>nơi Tsẽ là loại dữ liệu bạn muốn truyền vàoWork phương thức. Thật không may, một cái gì đó như thế này không tồn tại (chưa?).

Tuy nhiên, có một giải pháp được đề xuất cho vấn đề này. Nó liên quan đến việc tạo một lớp chứa cả hai, dữ liệu được truyền đến luồng cũng như phương thức đại diện cho phương thức worker như thế này:

public class ThreadWithState
{
    private string message;

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

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

Với phương pháp này, bạn sẽ bắt đầu chuỗi như thế này:

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

thread.Start();

Vì vậy, theo cách này, bạn chỉ cần tránh truyền xung quanh và có một cách an toàn để cung cấp dữ liệu cho một chuỗi ;-)


-2

đây là cách hoàn hảo ...

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);
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.