首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用SemaphoreSlim和字典等待NetMQ回复

使用SemaphoreSlim和字典等待NetMQ回复
EN

Stack Overflow用户
提问于 2019-08-18 17:39:07
回答 1查看 179关注 0票数 0

我有两个应用程序使用NetMQ相互通信。第一个应用程序内置了一个API控制器,第二个应用程序(引擎)在必要时与第一个应用程序来回通信。所有API控制器函数都是异步任务,因为我们期望同时发生许多请求。

其中一个API控制器函数需要首先向第二个应用程序(引擎)发送NetMQ消息,然后等待其响应,然后将结果返回给API函数请求者( API用户)。它需要以异步的方式完成所有这一切,而不需要从线程池中抓取,因为正如我前面所说的,我们可能会有大量的请求不断地访问这个控制器。

当SemaphoreSlim正在等待时,第二个应用程序(引擎)正在更新数据库中特定记录的状态,以及需要在API控制器函数中读取并返回给API用户的该记录的状态,如果我没有SemaphoreSlim等待设计,那么代码当然会将数据库记录读取到早期,而不是返回正确的状态。因此,解决方案是创建等待,允许引擎更新状态,然后通知API控制器函数,它可以从数据库读取新更新的状态。

一位同事通知我使用SemaphoreSlim和字典设置来通过锁定在函数中创建等待,并且只有当从引擎接收到NetMQ答复时,才从字典中释放NetMQ。然后,代码自然会继续,在那里我将检查数据库的更新状态,并将其返回给API用户。

问题是API控制器函数完全忽略SemaphoreSlim锁并过早地从数据库中读取。很可能是因为它是异步的,所以我的问题是,如何使SempahoreSlim锁和数据库代码中的读取以异步方式一起工作而不锁定线程?

我相信这些代码会让你更好地理解我想做的事情。

1. API控制器函数代码的一部分:

代码语言:javascript
复制
//Call Engine
Messenging.Queue.Enqueue(OrderPositionText + " " + UserId + " " + AssetPairId + " " + OrderType + " " + OrderRequestId + " " + rAmount + " " + rPrice + " " + Stop);

//Create SemaphoreSlim Lock and add it to global Dictionary     
var NewSemaphore = new SemaphoreSlim(1, 1);
await NewSemaphore.WaitAsync().ConfigureAwait(false);
ApiHub.UserSemaphoreDictionary.TryAdd(UserId, NewSemaphore);

//Lock was released lets now read record in Database then return appropriate result to API user.
                Debug.WriteLine("Checking order status for " + uuid);
                OrderRequest OrderRequestRec = await _context.OrderRequest.Where(x => x.UUID == uuid).FirstOrDefaultAsync();

    if (OrderRequestRec != null)
                    {
                        Debug.WriteLine("ORDER STATUS: " + OrderRequestRec.Status);
    }

2.这是另一个类中接收NetMQ消息并释放SemaphoreSlim:的代码

代码语言:javascript
复制
                        //API Semaphore Release lock
                        SemaphoreSlim checkUserId;
                        if (ApiHub.UserSemaphoreDictionary.TryGetValue(UserId, out checkUserId)) {
                            Debug.WriteLine("RELEASING LOCK: " + UserId);
                            checkUserId.Release();
                        }

结果是先读取数据库,然后发布SemaphoreSlim。我需要释放SemaphoreSlim,然后读取数据库。

编辑:--我遵循了下面的建议,并提出了这个工作代码,但我现在的问题是,这是异步的吗?它会在等待tc.Task时阻塞线程吗?

类:

代码语言:javascript
复制
  TaskCompletionSource<bool> checkUserId;
                                    if (ApiHub.UserSemaphoreDictionary.TryGetValue(UserId, out checkUserId))
                                    {
                                    Debug.WriteLine("RELEASING LOCK: " + UserId);
                                    checkUserId.SetResult(true);
                                    ApiHub.UserSemaphoreDictionary.Remove(UserId);
                                    }

控制器:

代码语言:javascript
复制
TaskCompletionSource<bool> tc = new TaskCompletionSource<bool>();
ApiHub.UserSemaphoreDictionary.TryAdd(UserId, tc);

    Debug.WriteLine("BEFORE TC");
    await tc.Task;
    Debug.WriteLine("AFTER TC");
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-08-18 17:49:55

对于这个场景,我使用TaskCompletionSource<bool>作为一种高级事件。它有一个Task属性,它是可直接访问的。然后,从接收消息的类中,您可以调用SetResult(true)来完成任务,并允许侍者继续工作。

注意,bool类型是无关的:任务必须有某种结果类型,但是这里我们只是使用它来表示事件已经发生(也就是说,我们只是想要一个Task等待,而不是一个Task<T>)。

编辑以响应OP的编辑:

是的,这是完全异步的。await关键字使C#编译器将控制器的代码分解为各个部分,因此await之后的部分成为“延续”,以后可以继续。它将继续添加到任务的完成处理程序列表中,然后放弃线程;控制器方法不再执行,因此不会耗尽线程/堆栈。

在调用SetResult的类中,这实际上会导致任务通知(执行)处理程序,从而导致控制器的代码苏醒并完成。无需创建额外的线程;使用现有线程,并在没有立即完成工作时立即放弃这些线程。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57546926

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档