c# - 停止读取 MSMQ 的服务

标签 c# msmq thread-abort

我是一名 Java 程序员,我被要求对 C# 应用程序进行一些更改。我已经使用 C# 工作了一个星期了,我终于遇到了一个问题,即查看文档没有任何帮助,而且我在 google 时找不到解决方案。

在这种情况下,我有一个 Windows 服务来处理到达 MSMQ 的消息。当收到一条消息时,当前正在监听的线程将其拾取并开始执行需要几秒钟的操作。

public void Start()
{
    this.listen = true;
    for (int i = 0; i < Constants.ThreadMaxCount; i++)
    {
        ThreadPool.QueueUserWorkItem(new WaitCallback(this.StartListening), i);
    }
    ...

private void StartListening(Object threadContext)
{

    int threadId = (int)threadContext;
    threads[threadId] = Thread.CurrentThread;
    PostRequest postReq;
    while(this.listen)
    {
        System.Threading.Monitor.Enter(locker);
        try
        {

            postReq = GettingAMessage();
        }
        finally
        {
            System.Threading.Monitor.Exit(locker);
        }
    }
    ...
}

GettingAMessage() 具有以下监听消息的行:

Task<Message> ts = Task.Factory.FromAsync<Message>
    (queue.BeginReceive(), queue.EndReceive);
ts.Wait();

问题是,当调用 Stop() 方法并且没有消息进入 MSMQ 时,所有线程都坐在那里等待消息。我尝试过使用超时,但这种方法对我来说似乎并不优雅(并且切换到任务工厂后,我不确定当前如何实现它们)。我对此的解决方案是将每个线程的引用添加到一个数组,这样我就可以取消它们。下面是每个工作线程创建后调用的。

threads[threadId] = Thread.CurrentThread;

然后应该被中止

public void Stop()
{
    try
    {
        this.listen = false;
        foreach(Thread a in threads) {
            a.Abort();
        }
    }
    catch
    {...}
}

关于为什么不关闭线程的任何建议? (或者更好的是,谁能告诉我应该在哪里寻找如何正确取消 ts.Wait() 的方法?)

最佳答案

使用 ManualResetEvent 类来正确、优雅地停止正在运行的线程。

此外,不要将ThreadPool 用于长时间运行的线程,请使用您自己创建的线程,否则,如果有大量长时间运行的任务,您可能会导致线程池饥饿,甚至可能导致死锁:

public class MsmqListener
{
    privatec ManualResetEvent _stopRequested = new ManualResetEvent(false);
    private List<Thread> _listenerThreads;
    private object _locker = new _locker();

    //-----------------------------------------------------------------------------------------------------

    public MsmqListener
    {
        CreateListenerThreads();
    }

    //-----------------------------------------------------------------------------------------------------

    public void Start()
    {
      StartListenerThreads();
    }

    //-----------------------------------------------------------------------------------------------------

    public void Stop()
    {
        try
        {
            _stopRequested.Set();
            foreach(Thread thread in _listenerThreads)
            {
                thread.Join(); // Wait for all threads to complete gracefully
            }
        }
        catch( Exception ex)
        {...}
    }

    //-----------------------------------------------------------------------------------------------------

    private void StartListening()
    {
            while( !_stopRequested.WaitOne(0) ) // Blocks the current thread for 0 ms until the current WaitHandle receives a signal
            {
                lock( _locker )
                {
                    postReq = GettingAMessage();
                }
            ...
    }

    //-----------------------------------------------------------------------------------------------------

    private void CreateListenerThreads()
    {
        _listenerThreads = new List<Thread>();
        for (int i = 0; i < Constants.ThreadMaxCount; i++)
        {
            listenerThread = new Thread(StartListening);
            listenerThreads.Add(listenerThread);
        }
    }

    //-----------------------------------------------------------------------------------------------------

    private void StartListenerThreads()
    {
        foreach(var thread in _listenerThreads)
        {
            thread.Start();
        }
    }
}

更新: 我将 AutoResetEvent 的使用更改为 ManualResetEvent 以支持停止多个等待线程(使用 ManualResetEvent,一旦发出信号,所有等待线程将收到通知并可以自由地继续他们的工作 - 在您的情况下停止收集消息)。

使用 volatile bool 不提供所有保证。它可能仍会读取过时的数据。最好使用底层操作系统同步机制,因为它提供了更强大的保证。来源:stackoverflow.com/a/11953661/952310

关于c# - 停止读取 MSMQ 的服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26848750/

相关文章:

C# PrintDocument 和打印机状态

c# - 在 Azure Function 中使用 Microsoft.WindowsAzure.Management.WebSites 时出错

msmq - 使用 MSMQ 的 MassTransit 发布/订阅

asp.net - 当调用ASP.NET System.Web.HttpResponse.End()时,当前线程是否中止?

c# - 重新抛出任务中的异常不会使任务进入故障状态

c# - Powershell - 确认 :$Y

c# - 如果源绑定(bind)适用,如何访问快速访问工具栏命令 `Add to Quick Access Tool`

具有 MSMQIntegrationBinding 的 WCF 不会从队列中获取消息

msmq - 远程队列地址的格式是什么

c# - 通过名称中止线程