private void RunAsync()
{
string param = "Hi";
Task.Run(() => MethodWithParameter(param));
}
private void MethodWithParameter(string param)
{
}
Biên tập
Do nhu cầu phổ biến, tôi phải lưu ý rằng trình Task
khởi chạy sẽ chạy song song với luồng gọi. Giả sử mặc định TaskScheduler
điều này sẽ sử dụng .NET ThreadPool
. Dù sao đi nữa, điều này có nghĩa là bạn cần tính đến bất kỳ (các) tham số nào được chuyển đến Task
có khả năng được nhiều luồng truy cập cùng một lúc, làm cho chúng ở trạng thái chia sẻ. Điều này bao gồm việc truy cập chúng trên chuỗi gọi.
Trong đoạn mã trên của tôi, trường hợp đó được thực hiện hoàn toàn tranh luận. Chuỗi là bất biến. Đó là lý do tại sao tôi sử dụng chúng làm ví dụ. Nhưng nói rằng bạn không sử dụng String
...
Một giải pháp là sử dụng async
và await
. Điều này, theo mặc định, sẽ nắm bắt SynchronizationContext
luồng đang gọi và sẽ tạo ra một phần tiếp theo cho phần còn lại của phương thức sau khi gọi tới await
và gắn nó vào phần đã tạo Task
. Nếu phương thức này đang chạy trên chuỗi WinForms GUI thì nó sẽ thuộc loại WindowsFormsSynchronizationContext
.
Phần tiếp theo sẽ chạy sau khi được đăng trở lại phần đã chụp SynchronizationContext
- chỉ một lần nữa theo mặc định. Vì vậy, bạn sẽ trở lại chuỗi mà bạn đã bắt đầu sau await
cuộc gọi. Bạn có thể thay đổi điều này theo nhiều cách khác nhau, đặc biệt là sử dụng ConfigureAwait
. Nói tóm lại, phần còn lại của phương pháp đó sẽ không tiếp tục cho đến khi sau khi các Task
đã hoàn thành trên thread khác. Nhưng luồng gọi sẽ tiếp tục chạy song song, không chỉ là phần còn lại của phương thức.
Việc chờ đợi này để hoàn tất việc chạy phần còn lại của phương thức có thể mong muốn hoặc không. Nếu không có gì trong phương thức đó sau này truy cập vào các tham số được truyền đến Task
mà bạn có thể không muốn sử dụng await
.
Hoặc có thể bạn sử dụng các tham số đó sau này trong phương thức. Không có lý do gì để await
ngay lập tức vì bạn có thể tiếp tục làm việc một cách an toàn. Hãy nhớ rằng, bạn có thể lưu trữ giá trị Task
trả về trong một biến và await
trên đó sau này - ngay cả trong cùng một phương thức. Ví dụ, một khi bạn cần truy cập các tham số đã truyền một cách an toàn sau khi thực hiện một số công việc khác. Một lần nữa, bạn không cần phải await
ở Task
bên phải khi bạn chạy nó.
Tuy nhiên, một cách đơn giản để làm cho chuỗi này an toàn đối với các tham số được truyền đến Task.Run
là thực hiện điều này:
Đầu tiên bạn phải trang trí RunAsync
bằng async
:
private async void RunAsync()
Lưu ý quan trọng
Tốt hơn là phương thức được đánh dấu không được trả về giá trị vô hiệu, như tài liệu liên kết đã đề cập. Ngoại lệ phổ biến cho điều này là các trình xử lý sự kiện chẳng hạn như nhấp vào nút và tương tự. Chúng phải trả về giá trị vô hiệu. Nếu không, tôi luôn cố gắng trả về một hoặc khi sử dụng . Đó là một thực hành tốt vì một số lý do.async
Task
Task<TResult>
async
Bây giờ bạn có thể await
chạy Task
như bên dưới. Bạn không thể sử dụng await
mà không có async
.
await Task.Run(() => MethodWithParameter(param));
Vì vậy, nói chung, nếu bạn thực await
hiện nhiệm vụ, bạn có thể tránh coi các tham số được truyền vào như một tài nguyên được chia sẻ tiềm năng với tất cả các cạm bẫy của việc sửa đổi thứ gì đó từ nhiều luồng cùng một lúc. Ngoài ra, hãy cẩn thận với việc đóng cửa . Tôi sẽ không trình bày sâu về những vấn đề đó nhưng bài viết được liên kết thực hiện rất tốt.
Ghi chú bên lề
Hơi lạc đề một chút, nhưng hãy cẩn thận khi sử dụng bất kỳ loại "chặn" nào trên chuỗi GUI của WinForms do nó được đánh dấu bằng [STAThread]
. Sử dụng await
sẽ không chặn, nhưng đôi khi tôi thấy nó được sử dụng kết hợp với một số loại chặn.
"Block" nằm trong dấu ngoặc kép vì về mặt kỹ thuật, bạn không thể chặn luồng GUI WinForms . Có, nếu bạn sử dụng lock
trên chuỗi WinForms GUI, nó sẽ vẫn bơm thông báo, mặc dù bạn nghĩ rằng nó bị "chặn". Nó không thể.
Điều này có thể gây ra các vấn đề kỳ lạ trong một số trường hợp rất hiếm. lock
Ví dụ, một trong những lý do bạn không bao giờ muốn sử dụng khi vẽ tranh. Nhưng đó là một trường hợp ngoài lề và phức tạp; tuy nhiên tôi đã thấy nó gây ra các vấn đề điên rồ. Vì vậy, tôi ghi nhận nó vì lợi ích hoàn chỉnh.