下面我有一个使用Azure Service Bus Queue的相当简单的.NET控制台应用程序。
正如您将看到的,我正在使用Task.Factory来启动25个接收器任务,然后调用我的APM样式的BeginMessageReceive方法。然后,在EndMessageReceive结束时,我再次调用BeginMessageReceive来保持循环。
我的问题是如何使用递归任务并可能使用C#5.0 async / await从APM样式的BeginMessageReceive / EndMessageReceive切换到TPL / TAP方法?
using System;
using System.Configuration;
using System.Threading.Tasks;
using Microsoft.ServiceBus.Messaging;
namespace ServiceBusConsumer
{
class Program
{
private static QueueClient _queueClient;
private static void Main(string[] args)
{
var connectionString = ConfigurationManager.AppSettings["Microsoft.ServiceBus.ConnectionString"];
_queueClient = QueueClient.CreateFromConnectionString(connectionString, "MyQueue");
for (var i = 0; i < 25; i++ )
{
Task.Factory.StartNew(BeginMessageReceive);
}
Console.WriteLine("Waiting for messages...");
Console.ReadKey();
_queueClient.Close();
} //end private static void Main(string[] args)
private static void BeginMessageReceive()
{
_queueClient.BeginReceive(TimeSpan.FromMinutes(5), EndMessageReceive, null);
}
private static void EndMessageReceive(IAsyncResult iar)
{
var message = _queueClient.EndReceive(iar);
try
{
if (message != null)
{
var msg = message.GetBody<string>();
Console.WriteLine("Message: " + msg);
if (_queueClient.Mode == ReceiveMode.PeekLock)
{
// Mark brokered message as completed at which point it's removed from the queue.
message.Complete();
}
}
}
catch (Exception ex)
{
if (_queueClient.Mode == ReceiveMode.PeekLock)
{
// unlock the message and make it available
message.Abandon();
}
Console.WriteLine("Exception was thrown: " + ex.GetBaseException().Message);
}
finally
{
if (message != null)
{
message.Dispose();
}
}
BeginMessageReceive();
}
}
}
如果MessageReceive超时到期,则再次以递归方式调用自身的新修改代码:
private static async Task MessageReceiveAsync()
{
while (true)
{
using (var message = await _queueClient.ReceiveAsync(TimeSpan.FromMinutes(5)))try
{
if (message != null)
{
try
{
var msg = message.GetBody<string>();
Console.WriteLine("Message: " + msg);
if (_queueClient.Mode == ReceiveMode.PeekLock)
{
// Mark brokered message as completed at which point it's removed from the queue.
await message.CompleteAsync();
}
}
catch (Exception ex)
{
message.AbandonAsync();
Console.WriteLine("Exception was thrown: " + ex.GetBaseException().Message);
}
}
}
}
}
答案 0 :(得分:4)
看起来Azure客户端库仍未使用TAP API进行更新。不知道持有的是什么......
无论如何,您可以使用TaskFactory.FromAsync
创建自己的APM->TAP wrappers,因此:
public static class MyAzureExtensions
{
public static Task<BrokeredMessage> ReceiveAsync(this QueueClient @this,
TimeSpan serverWaitTime)
{
return Task<BrokeredMessage>.Factory.FromAsync(
@this.BeginReceive, @this.EndReceive, serverWaitTime, null);
}
public static Task CompleteAsync(this BrokeredMessage @this)
{
return Task.Factory.FromAsync(@this.BeginComplete, @this.EndComplete, null);
}
}
将Azure调用封装到准备好TAP的API后,您可以这样使用它们:
private static void Main(string[] args)
{
var connectionString = ConfigurationManager.AppSettings["Microsoft.ServiceBus.ConnectionString"];
_queueClient = QueueClient.CreateFromConnectionString(connectionString, "MyQueue");
for (var i = 0; i < 25; i++ )
MyMessageReceiveAsync();
Console.WriteLine("Waiting for messages...");
Console.ReadKey();
_queueClient.Close();
}
private static async Task MyMessageReceiveAsync()
{
while (true)
{
using (var message = await _queueClient.ReceiveAsync(TimeSpan.FromMinutes(5)))
{
try
{
var msg = message.GetBody<string>();
Console.WriteLine("Message: " + msg);
if (_queueClient.Mode == ReceiveMode.PeekLock)
{
// Mark brokered message as completed at which point it's removed from the queue.
await message.CompleteAsync();
}
}
catch (Exception ex)
{
if (_queueClient.Mode == ReceiveMode.PeekLock)
{
// unlock the message and make it available
message.Abandon();
}
Console.WriteLine("Exception was thrown: " + ex.GetBaseException().Message);
}
}
}
}
使用async
这样的一个好处是不会不必要地占用线程池线程。原来用25个线程听;我的示例不会使用任何线程来监听。线程池线程在我的示例中被绑定的唯一时间是放弃消息(在错误处理分支中)。
与原始代码存在一个主要的语义差异:如果QueueClient
的“接收”引发异常,则在原始代码中会导致进程崩溃;在我的例子中,异常将被忽略。