C# 程序在访问消息队列时挂起

C# 程序在访问消息队列时挂起,c#,.net,multithreading,msmq,enterprise-library,C#,.net,Multithreading,Msmq,Enterprise Library,我有一个线程数组,它从私有消息队列检索消息,将它们反序列化为日志条目对象,并将日志条目对象的属性存储在SQL Server数据库表logentry中 下面是我创建和启动线程的代码 try { for (int i = 0; i < threads.Length; i++) { threads[i] = new Thread(new ThreadStart(this.logEntr

我有一个线程数组,它从私有消息队列检索消息,将它们反序列化为日志条目对象,并将日志条目对象的属性存储在SQL Server数据库表logentry中

下面是我创建和启动线程的代码

        try
        {
            for (int i = 0; i < threads.Length; i++)
            {
                threads[i] = new Thread(new ThreadStart(this.logEntriesToDatabase));
                threads[i].Start();
            }
        }
        catch (ThreadStateException ex)
        {
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK,MessageBoxIcon.Error);
            return;
        }
        catch (OutOfMemoryException ex)
        {
            MessageBox.Show("Not Enough Memory Please Close Other Applications To Continue", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            return;
        }
现在,每当我运行这个程序,消息队列变空时,程序就会挂起。 我似乎不明白为什么。我试图为q1.Receive()函数提供一个TimeSpan,但没有成功。我用180毫秒的时间调用了sleep方法,但它仍然不起作用。这可能是因为q1.Receive方法在遇到空队列时将当前线程发送到阻塞状态


请帮助我接受想法。

队列
为空时,您可能需要
中断
循环,而不是
继续
continue
导致无限循环,这将导致无响应行为

改变

根据评论编辑

您可以通过放置thread.Sleep来中断当前线程,以允许其他线程获得CPU共享。我在这里不使用Sleep,而是使用timer,在固定的时间间隔后对MSMQ执行操作

使用事件而不是计时器

使用定时器

void SumFun()
{
    aTimer = new System.Timers.Timer(10000);        
    aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
    aTimer.Interval = 2000;
    aTimer.Enabled = true;
}    
事件声明

private static void OnTimedEvent(object source, ElapsedEventArgs e)
{ 
    //Call the method for process MSMQ and do not use While loop to check queue.       
}

您可以使用/EndReceive异步读取消息,而不是在紧循环中同步读取消息并阻塞多个线程。有人提出了类似的问题

如果您使用的是.NET 4.0或更高版本,则可以从BeginReceive/EndReceive对创建任务,并使用ContinueWith处理消息,而无需创建新线程。在.NET 4.5中,您可以使用
asyc/await
关键字使处理更加简单,例如:

private async Task<Message> MyReceiveAsync()
{
    MessageQueue queue=new MessageQueue();
    ...
    var message=await Task.Factory.FromAsync<Message>(
                       queue.BeginReceive(),
                       queue.EndReceive);

    return message;
}

public async Task LogToDB()
{
    while(true)
    {
       var message=await MyReceiveAsync();
       SaveMessage(message);
    }
}

这样可以避免创建多个线程和计时器。

只需注意,您可以使用C语言进行注释#您不必在代码中添加区域垃圾信息,为什么不在while语句中使用条件
q1.GetAllMessages().Length>0
?(而不是
while(true)
我的意思是)更好的是,用正确命名的函数替换区域。使用如此多的区域是一种非常糟糕的代码,您应该使用
Using
块查看
IDisposable
模式。如果出现任何错误,您的数据库代码将泄漏句柄。即使一切正常,也可能在释放手柄方面落后。你说的“挂起来”是什么意思?你的主线挂起来了吗?如果是这样,它做了什么,您没有发布代码…所有线程都必须继续检查消息队列,并且必须在消息到达时做出响应。这就是为什么我不能打破这个循环。continue用于从头开始重新启动循环。您可以使用MessageQueue.BeginReceive/EndReceive异步接收消息,而不是使用阻塞线程。这样就不需要定时器或任何延迟
void SumFun()
{
    aTimer = new System.Timers.Timer(10000);        
    aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
    aTimer.Interval = 2000;
    aTimer.Enabled = true;
}    
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{ 
    //Call the method for process MSMQ and do not use While loop to check queue.       
}
private async Task<Message> MyReceiveAsync()
{
    MessageQueue queue=new MessageQueue();
    ...
    var message=await Task.Factory.FromAsync<Message>(
                       queue.BeginReceive(),
                       queue.EndReceive);

    return message;
}

public async Task LogToDB()
{
    while(true)
    {
       var message=await MyReceiveAsync();
       SaveMessage(message);
    }
}
public async Task LogToDB(CancellationToken token)
{
    while(!token.IsCancellationRequested)
    {
       var message=await MyReceiveAsync();
       SaveMessage(message);
    }
}