接收并发异步请求并一次处理一个

背景

我们有一个服务操作,可以接收并发的异步请求,并且必须一次处理一个请求。

在下面的示例中,该UploadAndImport(...)方法在多个线程上接收并发请求,但是对该ImportFile(...)方法的调用必须一次进行一次。

外行人说明

想象一下一个有很多工人(多线程)的仓库。人员(客户)可以同时(同时)向仓库发送许多包裹(请求)。当包裹进来时,工人要自始至终负责,丢下包裹的人可以离开(开火忘了)。工人的工作是将每个包裹放进一个小斜槽,一次只能有一个工人将一个包裹放进一个斜槽,否则会造成混乱。如果放下包裹的人员稍后再进站(轮询端点),则仓库应能够报告包裹是否从斜槽滑落。

问题

然后的问题是如何编写一个服务操作...

  1. 可以接收并发客户端请求,

  2. 在多个线程上接收并处理这些请求,

  3. 在收到请求的同一线程上处理请求,

  4. 一次处理一个请求,

  5. 是一种“一劳永逸”的操作,并且

  6. 有一个单独的轮询端点,该端点报告请求完成情况。

我们已经尝试了以下方法,并且想知道两件事:

  1. 有没有我们没有考虑过的竞争条件?

  2. 有没有一种更规范的方法可以在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中编写一个最小的可行示例,但这是一个有意义并可以编译的开始。


和以前一样,以上内容似乎很容易破解,我们既在询问其方法可能存在的陷阱,又在询问更合适/规范的替代模式。


当年话下
浏览 144回答 3
3回答
打开App,查看更多内容
随时随地看视频慕课网APP