2012-03-17 35 views
8

Mã sau đây là đơn giản hóa mã trong ứng dụng thực. Vấn đề bên dưới là một công việc dài sẽ được chạy trong chuỗi giao diện người dùng, thay vì một chuỗi nền.Cách nhận nhiệm vụ KHÔNG được thực thi trên luồng giao diện người dùng

void Do() 
    { 
     Debug.Assert(this.Dispatcher.CheckAccess() == true); 
     Task.Factory.StartNew(ShortUIWork, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()); 
    } 

    void ShortUIWork() 
    { 
     Debug.Assert(this.Dispatcher.CheckAccess() == true); 
     Task.Factory.StartNew(LongWork, TaskCreationOptions.LongRunning); 
    } 

    void LongWork() 
    { 
     Debug.Assert(this.Dispatcher.CheckAccess() == false); 
     Thread.Sleep(1000); 
    } 

Do Do() thường được gọi là ngữ cảnh giao diện người dùng. Và như vậy là ShortUIWork, như được định nghĩa bởi TaskScheduler. Tuy nhiên, LongWork kết thúc cũng được gọi là trong chuỗi giao diện người dùng, trong đó, tất nhiên, chặn giao diện người dùng.

Làm cách nào để đảm bảo rằng tác vụ không được chạy trong chuỗi giao diện người dùng?

+0

'tasks' tạo ra một chủ đề mà * sẽ không * chạy trên giao diện người dùng theo định nghĩa. Bạn có chắc chắn rằng chuỗi giao diện người dùng 'LongWork' chặn không? – Tigran

+2

@Tigran: TPL sử dụng chủ đề nền theo mặc định, không theo định nghĩa. –

+1

Mã không repro vấn đề không giúp được gì. Một lý do phổ biến cho hành vi như thế này là một lớp bao bọc cho một thành phần COM. COM giữ các đối tượng của các lớp tự quảng cáo để không hỗ trợ bất kỳ loại luồng nào an toàn bằng cách tự động sửa đổi bất kỳ cuộc gọi nào trở lại chuỗi đã tạo chúng. –

Trả lời

8

LongRunning chỉ là một gợi ý cho số TaskScheduler. Trong trường hợp của SynchronizationContextTaskScheduler (như được trả về bởi TaskScheduler.FromCurrentSynchronizationContext()), nó dường như bỏ qua gợi ý.

Một mặt điều này có vẻ không trực quan. Sau khi tất cả, nếu nhiệm vụ là chạy dài, nó không chắc bạn muốn nó chạy trên thread UI. Mặt khác, theo MSDN:

LongRunning - Chỉ định rằng một nhiệm vụ sẽ là một dài chạy, hạt thô hoạt động. Nó cung cấp một gợi ý cho TaskScheduler rằng oversubscription có thể được bảo hành.

Vì chuỗi giao diện người dùng không phải là chủ đề chuỗi chủ đề, không có "oversubscription" (thread pool starvation) có thể xảy ra, do đó có phần ý nghĩa rằng gợi ý sẽ không có hiệu lực cho SynchronizationContextTaskScheduler.

Bất kể, bạn có thể làm việc xung quanh vấn đề này bằng cách chuyển trở lại lên lịch công việc mặc định:

void ShortUIWork() 
{ 
    Debug.Assert(this.Dispatcher.CheckAccess() == true); 
    Task.Factory.StartNew(LongWork, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); 
} 
+0

Tại sao 'Debug.Assert (this.Dispatcher.CheckAccess() == false);' trong 'LongWork' vượt qua, mặc dù nó được thực hiện trên luồng UI? –

+0

@Branko: Tôi không theo. LongWork không thực hiện trên chuỗi giao diện người dùng nếu mã được thay đổi theo câu trả lời của tôi. Đó là lý do tại sao xác nhận chuyển (nhưng không thành công với mã ban đầu). –

+0

Lạ lùng, tôi có thể thề rằng nó đã trôi qua khi tôi gỡ lỗi ban đầu nhưng nó không thành công (trong mã ban đầu). Hoặc tôi đã nhấn một số tương tác gỡ lỗi lạ (bất cứ điều gì có thể được) hoặc đây chỉ là một suy não trên một phần của tôi;) Lời xin lỗi của tôi. –

Các vấn đề liên quan