当我从队列中处理工作项时,我刚陷入精神障碍
基本上,多个调用可以来到加载器类来加载文件(计算文件中的行数 - 例如,为了这个例子)。加载器应该对这些工作项进行排队并根据优先级对其进行处理。 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));
}
}
我做错了什么?
答案 0 :(得分:0)
首先,如果要按顺序处理项目,最好为该任务运行单独的线程,并将BlockingCollection
与ConcurrentPriorityQueue
一起使用(幸运的是,优先级队列确实支持正确的接口能够与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();
}
请注意,这仅适用于此示例。在实际使用中 - 多个线程将添加项目。
上面的代码只是一个例子,应该被改进以供实际使用(至少使用适当的异常处理)。