命名管道中的 C# 死锁

Posted

技术标签:

【中文标题】命名管道中的 C# 死锁【英文标题】:C# Deadlock in Named Pipe 【发布时间】:2018-02-24 20:51:50 【问题描述】:

我卡住了。我加入了使用命名管道的项目,并且可以说“不是理想的架构”。而且好像我不小心收到了死锁:(

逻辑如下。有命名管道。客户端和服务器模型。在服务器部分有一个循环,它总是 ping 命名管道并处理客户端发送的内容,有时会发回响应。

在我管道的客户端,我有来自其他开发人员的以下方法,用于向服务器发送请求并接收和返回响应。

private object locker = new Object();
private string ListenOnce(string msg)

    Debug.WriteLine("Will listen for message " + msg);

    string msgFrom = "";
    if (run) 
        string toReturn = "";
        lock (locker) 

            sw.WriteLine(msg); //Writing command to the pipes
            stream.WaitForPipeDrain(); //Waiting for another process to read the command
            msgFrom = sr.ReadLine(); //Reading
            toReturn = sr.ReadLine ();

            if (toReturn.Contains('¥'))
            
                string[] split = toReturn.Split('¥');

                if (split.Length > 1)
                
                    var roomNames = this.connection.application.GameCache.GetRoomNames();
                    for (int i = 1; i < split.Length; i++)
                    
                        string[] split2 = split[i].Split('¶');
                        if (split2.Length > 1)
                        
                            string accountName = split2[0];
                            int offenderActorID = int.Parse(split2[1]);
                            string offenderRoomName = split2[2];

                            foreach (var roomName in roomNames)
                            
                                Room room;
                                if (this.connection.application.GameCache.TryGetRoomWithoutReference(roomName, out room))
                                
                                    Game game = room as Game;

                                    if (game != null && game.Name == offenderRoomName)
                                    

                                        GameClientPeer peer = (GameClientPeer)game.ActorsManager.ActorsGetActorByNumber(offenderActorID).Peer;

                                        if (peer != null)
                                        
                                            peer.KickPlayer();
                                        
                                    
                                
                            
                        
                    
                
            
        
        if (toReturn.Contains('¥'))
        
            return toReturn.Split('¥')[0];
        
        else
        
            return toReturn;
        
    
    return "";

问题是 - 在某些情况下,我无法在请求时立即收到来自管道的响应,并且需要启动我在此处所说的“轮询器”。这是一个循环 5 次的任务,在这 5 次中,通过此 ListenOnce 方法“轮询”管道。

    private void PollTargets()
    
        timer.Dispose();
        Debug.WriteLine("Going to start polling");
        Task.Factory.StartNew(() => 
            int runCount = 0;
            while (true)
            
                runCount++;
                PipeOperation request = new PipeOperation(Consts.Pipes.RequestTargets, uniqueID);
                string responseStr = unityConnection.client.SendMessage(JsonConvert.SerializeObject(request));
                Debug.WriteLine("Task is running, response is " + responseStr);

                if (!string.IsNullOrEmpty(responseStr))
                
                    try
                    
                        PipeOperation pipeResponse = JsonConvert.DeserializeObject<PipeOperation>(responseStr);
                        if (!string.IsNullOrEmpty(pipeResponse.Payload))
                        
                            GrenadeExplosionData explosionData = JsonConvert.DeserializeObject<GrenadeExplosionData>(pipeResponse.Payload);
                            if (explosionData != null)
                            
                                //probably need to invoke that in main thread
                                DealDamage(explosionData);
                                //isRunning = false;
                                Debug.WriteLine("Received nice response, will damage targets");
                                break;
                            
                        
                    
                    catch (Exception exc)
                    
                        Debug.WriteLine("Something went wrong while polling...");
                        Debug.WriteLine(exc.Message);
                        break;
                    
                

                if (runCount > 5)
                
                    Debug.WriteLine("run count exceed " + runCount.ToString());
                    break;
                
            
            RemoveGrenadeFromUnityConnection();
        );
    

当手榴弹爆炸时,我正在从这样的计时器开始轮询:

        timer = new System.Threading.Timer((obj) =>
        
            PollTargets();
        ,
            null, 4000, System.Threading.Timeout.Infinite); 

就是这样。人玩2-3小时后。好像我收到了僵局。应该考虑到服务器上可能有许多手榴弹启动了那个轮询器,所以它可能只是在那里的某个时候发疯了。 请帮忙,我坚持这一点。谁有想法? 我们应该记住,

        sw.WriteLine(msg); //Writing command to the pipes
        stream.WaitForPipeDrain();
        msgFrom = sr.ReadLine(); //Reading
        toReturn = sr.ReadLine ();

一次只能由一个线程使用,因为流可能只能从一个源读取。

代码中有多次对 ListenOnce 的调用,但不是很多。每 4 分钟发射一次。其余的不是恒定的,而是有条件的。

希望有人能看看这里哪里出错了......

【问题讨论】:

获取tour、阅读How to Ask和minimal reproducible example。您需要 MCVE 来获得调试帮助。定义死锁在这种情况下的含义。是否所有客户端都在等待来自服务器的响应?服务器处于什么状态?您是否尝试将调试器附加到服务器和其中一个停滞的客户端以调查其内部状态? 谢谢,会的!我试图附加调试器,实际上正在发生什么 - 在某些时候它到达了它的位置 lock(locker) 并且根本没有走得更远......应用程序本身没有被挂起,它只是没有走得更远跨度> 【参考方案1】:

找到了锁定一切的东西......但是,它并没有多大帮助:) 它的 stream.WaitForPipeDrain();

它试图读取管道的另一端,但由于消息模式没有超时,它永远挂起..

【讨论】:

以上是关于命名管道中的 C# 死锁的主要内容,如果未能解决你的问题,请参考以下文章

C# 中的命名管道输入输出

使用命名管道时如何防止死锁?

命名管道(C#)

命名管道:C# 服务器、C++ 客户端

C# 和 python 之间的命名管道

C++ 中的序列化和 C# 中的反序列化,对于命名管道,反之亦然