Task.Delay(250).Wait()
Bạn biết bạn đang làm điều gì sai khi bạn sử dụng Wait
trong mã bạn đang cố gắng để làm cho đồng bộ. Đó là một chủ đề lãng phí không làm gì cả.
Sau đây sẽ là tốt hơn nhiều:
lastTask = lastTask.ContinueWith(t =>
{
// do some task
}).ContinueWith(t => Task.Delay(250)).Unwrap();
ContinueWith
trả về một Task<Task>
, và cuộc gọi Unwrap
biến đó vào một Task
đó sẽ hoàn thành khi nhiệm vụ nội làm.
Bây giờ, để trả lời câu hỏi của bạn, chúng ta hãy nhìn vào những gì trình biên dịch tạo:
public void DoSomeTask()
{
if (this.lastTask == null)
this.lastTask = (Task) Task.FromResult<bool>(false);
// ISSUE: method pointer
// ISSUE: method pointer
this.lastTask = this.lastTask
.ContinueWith(
Program.<>c.<>9__2_0
?? (Program.<>c.<>9__2_0 = new Action<Task>((object) Program.<>c.<>9, __methodptr(<DoSomeTask>b__2_0))))
.ContinueWith<Task>(
Program.<>c.<>9__2_1
?? (Program.<>c.<>9__2_1 = new Func<Task, Task>((object) Program.<>c.<>9, __methodptr(<DoSomeTask>b__2_1))))
.Unwrap();
}
[CompilerGenerated]
[Serializable]
private sealed class <>c
{
public static readonly Program.<>c <>9;
public static Action<Task> <>9__2_0;
public static Func<Task, Task> <>9__2_1;
static <>c()
{
Program.<>c.<>9 = new Program.<>c();
}
public <>c()
{
base.\u002Ector();
}
internal void <DoSomeTask>b__2_0(Task t)
{
}
internal Task <DoSomeTask>b__2_1(Task t)
{
return Task.Delay(250);
}
}
này được dịch ngược với dotPeek trong "cho tôi tất cả can đảm" chế độ.
Nhìn vào phần này:
.ContinueWith<Task>(
Program.<>c.<>9__2_1
?? (Program.<>c.<>9__2_1 = new Func<Task, Task>((object) Program.<>c.<>9, __methodptr(<DoSomeTask>b__2_1))))
Chức năng ContinueWith
được đưa ra một đại biểu singleton. Vì vậy, không có đóng cửa trên bất kỳ biến ở đó.
Bây giờ, có chức năng này:
internal Task <DoSomeTask>b__2_1(Task t)
{
return Task.Delay(250);
}
Các t
đây là một tham chiếu đến các nhiệm vụ trước. Chú ý một cái gì đó? Nó không bao giờ được sử dụng. JIT sẽ đánh dấu địa phương này là không thể truy cập được, và GC sẽ có thể làm sạch nó. Khi tối ưu hóa được kích hoạt, JIT sẽ đánh dấu tích cực những người dân địa phương đủ điều kiện để thu thập, thậm chí đến mức một phương pháp thể hiện có thể thực hiện trong khi trường hợp đang được GC thu thập, nếu phương pháp thể hiện không tham chiếu this
trong mã còn lại để thực thi.
Bây giờ, một điều cuối cùng, có trường m_parent
trong lớp Task
, điều này không tốt cho kịch bản của bạn. Nhưng miễn là bạn không sử dụng TaskCreationOptions.AttachedToParent
bạn sẽ ổn thôi. Bạn luôn có thể thêm cờ DenyChildAttach
để tăng thêm sự an toàn và tự tài liệu.
Đây là function which deals with that:
internal static Task InternalCurrentIfAttached(TaskCreationOptions creationOptions)
{
return (creationOptions & TaskCreationOptions.AttachedToParent) != 0 ? InternalCurrent : null;
}
Vì vậy, bạn nên an toàn ở đây. Nếu bạn muốn được chắc chắn, hãy chạy trình thu thập bộ nhớ trên một chuỗi dài và tự mình xem.
Bạn có thể giả định rằng 'DoSomeTask()' sẽ không được gọi song song? –
@YacoubMassad Nó sẽ không ảnh hưởng đến các câu hỏi cụ thể mà anh ta hỏi về ngay cả khi anh ta. – Servy
Bất kỳ cơ hội nào bạn có thể sử dụng 'async'/'await'? Mã sẽ được cách đơn giản và dễ dàng hơn để lý do. –