从优先级队列异步处理工作项

时间:2017-03-16 16:13:42

标签: c# asynchronous

当我从队列中处理工作项时,我刚陷入精神障碍

基本上,多个调用可以来到加载器类来加载文件(计算文件中的行数 - 例如,为了这个例子)。加载器应该对这些工作项进行排队并根据优先级对其进行处理。 Enqueing可以并行工作,但文件的actaul加载应该按顺序进行。

但是我的代码同步工作并按顺序执行整个代码。

 public class RequestInfo
    {
        public string filename;
        public ManualResetEvent ManaulEvent;
        public int result;
        public int priority;
    }

    public class Loader
    {
        readonly ConcurrentPriorityQueue<int, RequestInfo> _loadQueue = new ConcurrentPriorityQueue<int, RequestInfo>();
        public async Task<int> NumOfLinesinFile(string filepath, int priority)
        {
            RequestInfo info = new RequestInfo()
            {
                filename = filepath,
                ManaulEvent = new ManualResetEvent(false),
                priority = priority
            };
            _loadQueue.Enqueue(info.priority, info);
            var task = Task.Factory.StartNew(() =>
            {
                LoadContent(info);
                info.ManaulEvent.WaitOne();
            });
            await task;
            return info.result;
        }

        async void  LoadContent(RequestInfo info)
        {
            await Task.Run(() => DoWork());
        }

        void DoWork()
        {
            KeyValuePair<int, RequestInfo> quueueContent;
            bool val = _loadQueue.TryDequeue(out quueueContent);
            if (val)
            {
                quueueContent.Value.result = Load(quueueContent.Value.filename, quueueContent.Value.priority);
                quueueContent.Value.ManaulEvent.Set();
            }
        }

        int Load(string filename, int priority)
        {
            Thread.Sleep(500);
            return priority;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            DoFileLoad();
            Console.ReadKey();
        }
        private static async void DoFileLoad()
        {
            Loader loader = new Loader();

            Console.WriteLine(await loader.NumOfLinesinFile("C://file1.txt", 1));
            Console.WriteLine(await loader.NumOfLinesinFile("C://file2.txt", 33));
            Console.WriteLine(await loader.NumOfLinesinFile("C://fil3.txt", 0));
            Console.WriteLine(await loader.NumOfLinesinFile("C://fil3.txt", 6));
        }
    }

我做错了什么?

1 个答案:

答案 0 :(得分:0)

首先,如果要按顺序处理项目,最好为该任务运行单独的线程,并将BlockingCollectionConcurrentPriorityQueue一起使用(幸运的是,优先级队列确实支持正确的接口能够与BlockingCollection一起使用。您不需要像现在一样为每个项目启动新线程。

其次,您可以在案例中使用ManualResetEvent,而不是TaskCompletionSource。这是一个例子:

public class RequestInfo
{
    public string Filename { get; set; }
    public int Result { get; set; }
    public int Priority { get; set; }
    internal TaskCompletionSource<int> TaskSource { get; set; }        
}

public class Loader : IDisposable
{
    readonly BlockingCollection<KeyValuePair<int, RequestInfo>> _loadQueue = new BlockingCollection<KeyValuePair<int, RequestInfo>>(new ConcurrentPriorityQueue<int, RequestInfo>());
    public Loader() {
        new Thread(Loop)
        {
            IsBackground = true
        }.Start();
    }

    public Task<int> NumOfLinesinFile(string filepath, int priority) {
        RequestInfo info = new RequestInfo() {
            Filename = filepath,
            TaskSource = new TaskCompletionSource<int>(),
            Priority = priority
        };
        _loadQueue.TryAdd(new KeyValuePair<int, RequestInfo>(info.Priority, info));
        return info.TaskSource.Task;
    }

    void Loop()
    {
        foreach (var item in _loadQueue.GetConsumingEnumerable()) {
            item.Value.Result = Load(item.Value.Filename, item.Value.Priority);
            item.Value.TaskSource.SetResult(item.Value.Result);
        }                                                    
    }

    int Load(string filename, int priority)
    {
        Thread.Sleep(1000);
        return priority;
    }

    public void Dispose() {
        _loadQueue.CompleteAdding();
        _loadQueue.Dispose();
    }
}

然后,在您的示例中,您将添加每个项目并等待返回的任务。这意味着将添加项目,然后将对其进行处理,然后才会添加下一个项目。如果你想避免这种情况 - 改变你的代码:

private static async void DoFileLoad() {
    Loader loader = new Loader();
    var tasks = new List<Task<int>>();
    tasks.Add(loader.NumOfLinesinFile("C://file1.txt", 1));
    tasks.Add(loader.NumOfLinesinFile("C://file2.txt", 33));
    tasks.Add(loader.NumOfLinesinFile("C://fil3.txt", 0));
    tasks.Add(loader.NumOfLinesinFile("C://fil3.txt", 6));
    while (tasks.Count > 0) {
        var t = await Task.WhenAny(tasks);
        Console.WriteLine(t.Result);
        tasks.Remove(t);
    }

    Console.ReadKey();
    loader.Dispose();
}

请注意,这仅适用于此示例。在实际使用中 - 多个线程将添加项目。

上面的代码只是一个例子,应该被改进以供实际使用(至少使用适当的异常处理)。

相关问题