Cho đến bây giờ tôi đã sử dụng tác vụ LongRunning TPL cho công việc nền liên kết CPU theo chu kỳ thay vì bộ đếm thời gian phân luồng, bởi vì:
- nhiệm vụ TPL hỗ trợ hủy bỏ
- bộ đếm thời gian phân luồng có thể bắt đầu một luồng khác trong khi chương trình đang tắt gây ra sự cố có thể xảy ra với tài nguyên đã xử lý
- cơ hội cho việc chạy quá mức: bộ đếm thời gian phân luồng có thể bắt đầu một chuỗi khác trong khi chuỗi trước đó vẫn đang được xử lý do công việc dài không mong muốn (tôi biết, có thể ngăn chặn bằng cách dừng và khởi động lại bộ hẹn giờ)
Tuy nhiên, giải pháp TPL luôn yêu cầu một luồng chuyên dụng không cần thiết trong khi chờ hành động tiếp theo (hầu hết thời gian). Tôi muốn sử dụng giải pháp được đề xuất của Jeff để thực hiện công việc tuần hoàn ràng buộc CPU trên nền bởi vì nó chỉ cần một luồng threadpool khi có việc phải làm, điều này tốt hơn cho khả năng mở rộng (đặc biệt khi khoảng thời gian lớn).
Để đạt được điều đó, tôi sẽ đề xuất 4 cách điều chỉnh:
- Thêm
ConfigureAwait(false)
vào Task.Delay()
để thực hiện doWork
hành động trên luồng nhóm luồng, nếu không doWork
sẽ được thực hiện trên luồng đang gọi mà không phải là ý tưởng của song song
- Bám sát mẫu hủy bằng cách ném TaskCanceledException (vẫn bắt buộc?)
- Chuyển tiếp CancelToken đến
doWork
để cho phép nó hủy tác vụ
- Thêm một tham số của đối tượng kiểu để cung cấp thông tin trạng thái tác vụ (như tác vụ TPL)
Về điểm 2, tôi không rõ, liệu async await vẫn yêu cầu TaskCanceledExecption hay chỉ là phương pháp hay nhất?
public static async Task Run(Action<object, CancellationToken> doWork, object taskState, TimeSpan period, CancellationToken cancellationToken)
{
do
{
await Task.Delay(period, cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
doWork(taskState, cancellationToken);
}
while (true);
}
Hãy đóng góp ý kiến của bạn cho giải pháp được đề xuất ...
Cập nhật 2016-8-30
Giải pháp trên không gọi ngay lập tức doWork()
mà bắt đầu với await Task.Delay().ConfigureAwait(false)
để đạt được chuyển đổi luồng cho doWork()
. Giải pháp dưới đây khắc phục sự cố này bằng cách gói doWork()
cuộc gọi đầu tiên trong một Task.Run()
và chờ nó.
Dưới đây là thay thế async \ await được cải tiến để Threading.Timer
thực hiện công việc theo chu kỳ có thể hủy bỏ và có thể mở rộng (so với giải pháp TPL) vì nó không chiếm bất kỳ luồng nào trong khi chờ hành động tiếp theo.
Lưu ý rằng ngược lại với Timer, thời gian chờ ( period
) là không đổi và không phải là thời gian chu kỳ; thời gian chu kỳ là tổng của thời gian chờ đợi và thời gian doWork()
có thể thay đổi.
public static async Task Run(Action<object, CancellationToken> doWork, object taskState, TimeSpan period, CancellationToken cancellationToken)
{
await Task.Run(() => doWork(taskState, cancellationToken), cancellationToken).ConfigureAwait(false);
do
{
await Task.Delay(period, cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
doWork(taskState, cancellationToken);
}
while (true);
}