我正在尝试找出处理和正确处理以下内容的最佳做法: 我有一个内联方法,在方法的过程中添加一个事件处理程序进程,但我需要弄清楚如何处理我的方法的其余部分,等待它继续之前接收数据到该变量(或者如果有更好的可能的做法)
目前我关注的方法是一个内联方法,所以它完成并尝试在分配给它之前从预期变量返回数据,因此它返回'null',因为它试图在之前返回它该事件处理程序获取数据。
请让我知道处理/完成此操作的最佳做法/模式。
谢谢。
[代码示例]我正在调用的方法,并且正在完成并尝试在分配之前返回'recievedMsg'变量值...
public T Recieve<T>(string routingKey)
{
T recievedMsg = default(T);
try
{
_channel.QueueDeclare(queue: routingKey,
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
var consumer = new EventingBasicConsumer(_channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
recievedMsg = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(message);
};
_channel.BasicConsume(queue: routingKey,
noAck: true,
consumer: consumer);
}
catch (Exception ex)
{
// Error Handling
}
return recievedMsg;
}
[调用方法调用]
var obj = new testObj()
{
Id = 123,
Name = "Test McTesting",
Dexcription = "Employee of the Month"
};
using (var rbt= new proxyClass("localhost"))
{
// .Recieve() method is completing too quickly so always returns null to employee
var employee = rbt.Recieve<testObj>("Test McTesting");
}
答案 0 :(得分:2)
直接回答问题(关于模式);我建议使用任务异步模式(TAP)。 https://msdn.microsoft.com/en-us/library/hh873175(v=vs.110).aspx
TAP不仅可以处理成功的结果,还可以简化异常处理;使用简单的AutoResetEvent
或ManualResetEvent
,这不是一件轻而易举的事。
TAP还允许您轻松执行诸如为操作添加超时或允许用户取消等操作。
public async Task<T> Recieve<T>(string routingKey)
{
var tcs = new TaskCompletionSource<T>();
_channel.QueueDeclare(queue: routingKey,
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
var consumer = new EventingBasicConsumer(_channel);
consumer.Received += (model, ea) =>
{
try
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
var recievedMsg = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(message);
// Tries to set the result. Will fail if the task has been cancelled
tcs.TrySetResult(recievedMsg);
}
catch (Exception ex)
{
// Tries to set the exception. Will fail if the task has been cancelled
tcs.TrySetException(ex);
}
};
_channel.BasicConsume(queue: routingKey,
noAck: true,
consumer: consumer);
return await tcs.Task;
}
示例用法(在async
方法内)
public async Task TestIt()
{
using (var rbt = new ProxyClass("localhost"))
{
var employee = await rbt.Recieve<TestObj>("Test McTesting");
Console.WriteLine($"{employee.Id} / {employee.Name} / {employee.Dexcription}");
}
}
(奖励:超时使用示例 - 这是TAP的优势之一)
public async Task TestItWithTimeout()
{
using (var rbt = new ProxyClass("localhost"))
{
var task = rbt.Recieve<TestObj>("Test McTesting");
if (await Task.WhenAny(task, Task.Delay(TimeSpan.FromSeconds(30))) == task)
{
var employee = await task;
Console.WriteLine($"{employee.Id} / {employee.Name} / {employee.Dexcription}");
}
}
}
注意:将try..catch
置于EventingBasicConsumer.Received
处理程序中是个好主意,因为RabbitMq.Client的使用者线程不会处理异常 - 线程将会死掉。
如果您的Rabbit Consumer线程意外死亡而您没有处理错误,那么您的应用程序将无法知道它 - 所有后续消息只是在代理的队列中未被传递,并且随后调用您的{{1只会永远等待。