我们有一个服务操作,可以接收并发的异步请求,并且必须一次处理一个请求。
在下面的示例中,该UploadAndImport(...)
方法在多个线程上接收并发请求,但是对该ImportFile(...)
方法的调用必须一次进行一次。
想象一下一个有很多工人(多线程)的仓库。人员(客户)可以同时(同时)向仓库发送许多包裹(请求)。当包裹进来时,工人要自始至终负责,丢下包裹的人可以离开(开火忘了)。工人的工作是将每个包裹放进一个小斜槽,一次只能有一个工人将一个包裹放进一个斜槽,否则会造成混乱。如果放下包裹的人员稍后再进站(轮询端点),则仓库应能够报告包裹是否从斜槽滑落。
然后的问题是如何编写一个服务操作...
可以接收并发客户端请求,
在多个线程上接收并处理这些请求,
在收到请求的同一线程上处理请求,
一次处理一个请求,
是一种“一劳永逸”的操作,并且
有一个单独的轮询端点,该端点报告请求完成情况。
我们已经尝试了以下方法,并且想知道两件事:
有没有我们没有考虑过的竞争条件?
有没有一种更规范的方法可以在C#.NET中使用面向服务的体系结构(我们恰好使用WCF)对这种情况进行编码?
这是我们尝试过的服务代码。它的工作原理虽然听起来有些像是乱砍或l头。
static ImportFileInfo _inProgressRequest = null;
static readonly ConcurrentDictionary<Guid, ImportFileInfo> WaitingRequests =
new ConcurrentDictionary<Guid, ImportFileInfo>();
public void UploadAndImport(ImportFileInfo request)
{
// Receive the incoming request
WaitingRequests.TryAdd(request.OperationId, request);
while (null != Interlocked.CompareExchange(ref _inProgressRequest, request, null))
{
// Wait for any previous processing to complete
Thread.Sleep(500);
}
// Process the incoming request
ImportFile(request);
Interlocked.Exchange(ref _inProgressRequest, null);
WaitingRequests.TryRemove(request.OperationId, out _);
}
public bool UploadAndImportIsComplete(Guid operationId) =>
!WaitingRequests.ContainsKey(operationId);
这是示例客户端代码。
private static async Task UploadFile(FileInfo fileInfo, ImportFileInfo importFileInfo)
{
using (var proxy = new Proxy())
using (var stream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read))
{
importFileInfo.FileByteStream = stream;
proxy.UploadAndImport(importFileInfo);
}
await Task.Run(() => Poller.Poll(timeoutSeconds: 90, intervalSeconds: 1, func: () =>
{
using (var proxy = new Proxy())
{
return proxy.UploadAndImportIsComplete(importFileInfo.OperationId);
}
}));
}
很难在Fiddle中编写一个最小的可行示例,但这是一个有意义并可以编译的开始。
和以前一样,以上内容似乎很容易破解,我们既在询问其方法可能存在的陷阱,又在询问更合适/规范的替代模式。
相关分类