거기를 비동기 적으로 대리자 큐를 실행합니까?
사용자 지정 작업 스케줄러로 구현하고 싶습니다. 그런 다음 대리인을 작업으로 대기열에 넣고 실행하면 예외 처리, 취소 및 async/await
의 모든 이점을 얻을 수 있습니다.
대리인을 연속 주문으로 실행하는 작업 스케줄러 구현은 BlockingCollection
을 사용하여 매우 간단합니다. 아래의 SerialTaskScheduler
는 Stephen Toub's StaTaskScheduler
의 단순화 된 버전입니다 :
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Console_21628490
{
// Test
class Program
{
static async Task DoWorkAsync()
{
using (var scheduler = new SerialTaskScheduler())
{
var tasks = Enumerable.Range(1, 10).Select(i =>
scheduler.Run(() =>
{
var sleep = 1000/i;
Thread.Sleep(sleep);
Console.WriteLine("Task #" + i + ", sleep: " + sleep);
}, CancellationToken.None));
await Task.WhenAll(tasks);
}
}
static void Main(string[] args)
{
DoWorkAsync().Wait();
Console.ReadLine();
}
}
// SerialTaskScheduler
public sealed class SerialTaskScheduler : TaskScheduler, IDisposable
{
Task _schedulerTask;
BlockingCollection<Task> _tasks;
Thread _schedulerThread;
public SerialTaskScheduler()
{
_tasks = new BlockingCollection<Task>();
_schedulerTask = Task.Run(() =>
{
_schedulerThread = Thread.CurrentThread;
foreach (var task in _tasks.GetConsumingEnumerable())
TryExecuteTask(task);
});
}
protected override void QueueTask(Task task)
{
_tasks.Add(task);
}
protected override IEnumerable<Task> GetScheduledTasks()
{
return _tasks.ToArray();
}
protected override bool TryExecuteTaskInline(
Task task, bool taskWasPreviouslyQueued)
{
return _schedulerThread == Thread.CurrentThread &&
TryExecuteTask(task);
}
public override int MaximumConcurrencyLevel
{
get { return 1; }
}
public void Dispose()
{
if (_schedulerTask != null)
{
_tasks.CompleteAdding();
_schedulerTask.Wait();
_tasks.Dispose();
_tasks = null;
_schedulerTask = null;
}
}
public Task Run(Action action, CancellationToken token)
{
return Task.Factory.StartNew(action, token, TaskCreationOptions.None, this);
}
public Task Run(Func<Task> action, CancellationToken token)
{
return Task.Factory.StartNew(action, token, TaskCreationOptions.None, this).Unwrap();
}
public Task<T> Run<T>(Func<Task<T>> action, CancellationToken token)
{
return Task.Factory.StartNew(action, token, TaskCreationOptions.None, this).Unwrap();
}
}
}
출력 :
이
Task #1, sleep: 1000
Task #2, sleep: 500
Task #3, sleep: 333
Task #4, sleep: 250
Task #5, sleep: 200
Task #6, sleep: 166
Task #7, sleep: 142
Task #8, sleep: 125
Task #9, sleep: 111
Task #10, sleep: 100
어떤 부분을 정확히 비동기해야 하는가? 대기열? 또는 액션 자체가 비동기가되어야합니까? – i3arnon
@ I3arnon 그것은 분명히해야합니다. 작업이 비동기 적으로 실행되어야합니다. 대기열 작업이 동 기적으로 실행되어야하며, 필수적이지는 않지만 명령이 잠재적으로 변경 될 수 있습니다. – Ashigore
@ I3arnon 분명하지 않은지는 잘 모르겠습니다. 내 말은 작업 대기열에 넣기 전까지는 대기열 작업이 차단되어야한다는 것입니다. 그 후에 모든 것은 동작의 실행을 포함하여 다른 스레드에서 발생해야합니다. 하지만 한 번에 하나씩 실행해야합니다. – Ashigore