Có mẫu được thiết lập đề xuất nào để tự hủy và khởi động lại tác vụ không?Mẫu để tự hủy và khởi động lại tác vụ
Ví dụ: tôi đang làm việc trên API cho trình kiểm tra chính tả nền. Phiên kiểm tra chính tả được bao bọc dưới dạng Task
. Mỗi phiên mới phải hủy bỏ phiên bản trước đó và chờ kết thúc (để sử dụng lại đúng tài nguyên như nhà cung cấp dịch vụ kiểm tra chính tả, v.v.).
tôi đã đi lên với một cái gì đó như thế này:
class Spellchecker
{
Task pendingTask = null; // pending session
CancellationTokenSource cts = null; // CTS for pending session
// SpellcheckAsync is called by the client app
public async Task<bool> SpellcheckAsync(CancellationToken token)
{
// SpellcheckAsync can be re-entered
var previousCts = this.cts;
var newCts = CancellationTokenSource.CreateLinkedTokenSource(token);
this.cts = newCts;
if (IsPendingSession())
{
// cancel the previous session and wait for its termination
if (!previousCts.IsCancellationRequested)
previousCts.Cancel();
// this is not expected to throw
// as the task is wrapped with ContinueWith
await this.pendingTask;
}
newCts.Token.ThrowIfCancellationRequested();
var newTask = SpellcheckAsyncHelper(newCts.Token);
this.pendingTask = newTask.ContinueWith((t) => {
this.pendingTask = null;
// we don't need to know the result here, just log the status
Debug.Print(((object)t.Exception ?? (object)t.Status).ToString());
}, TaskContinuationOptions.ExecuteSynchronously);
return await newTask;
}
// the actual task logic
async Task<bool> SpellcheckAsyncHelper(CancellationToken token)
{
// do not start a new session if the the previous one still pending
if (IsPendingSession())
throw new ApplicationException("Cancel the previous session first.");
// do the work (pretty much IO-bound)
try
{
bool doMore = true;
while (doMore)
{
token.ThrowIfCancellationRequested();
await Task.Delay(500); // placeholder to call the provider
}
return doMore;
}
finally
{
// clean-up the resources
}
}
public bool IsPendingSession()
{
return this.pendingTask != null &&
!this.pendingTask.IsCompleted &&
!this.pendingTask.IsCanceled &&
!this.pendingTask.IsFaulted;
}
}
Ứng dụng client (UI) chỉ nên có thể gọi SpellcheckAsync
nhiều lần như mong muốn, mà không lo lắng về việc hủy một phiên chưa giải quyết. Vòng lặp chính doMore
chạy trên luồng giao diện người dùng (vì nó liên quan đến giao diện người dùng, trong khi tất cả các cuộc gọi nhà cung cấp dịch vụ kiểm tra chính tả là IO-bound).
Tôi cảm thấy hơi khó chịu về việc tôi phải chia API thành hai peices, SpellcheckAsync
và SpellcheckAsyncHelper
, nhưng tôi không thể nghĩ ra cách tốt hơn để làm điều này và chưa được thử nghiệm.
@Stephen Cleary, tôi rất tôn trọng công việc của bạn trên mọi thứ không đồng bộ, vì vậy xin đừng hiểu nhầm điều này: Tôi chỉ tò mò thôi. Tôi hơi ngạc nhiên khi bạn không viết lại phần 'await this.pendingTask' bằng cách sử dụng 'SemaphoreSlim' hoặc của riêng bạn' AsyncLock' hoặc tương tự. Bạn có thường tin rằng việc cải thiện an toàn luồng trong các phần "đồng bộ" của các phương pháp không đồng bộ là một tối ưu hóa sớm không? –
@KirillShlenskiy: Không có gì sai khi sử dụng 'SemaphoreSlim' hoặc tương tự với giới hạn một lần. –