多次 SignalR 调用使 UI 没有响应

Posted

技术标签:

【中文标题】多次 SignalR 调用使 UI 没有响应【英文标题】:Several SignalR calls makes the UI not responding 【发布时间】:2018-03-03 00:52:57 【问题描述】:

我们确实有一个任务调度程序应用程序的 Execute 方法,它处理接收到的输出字符串,购买一个 Process (System.Diagnostics):

    public override void Execute()
    
        // ... more logic above 

        Process = new Process  StartInfo =  FileName = fileName, Arguments = args  ;
        Process.StartInfo.UseShellExecute = false;
        Process.StartInfo.RedirectStandardOutput = true;
        Process.StartInfo.RedirectStandardError = true;
        Process.EnableRaisingEvents = true;
        Process.Exited += ProcessExited;
        Process.OutputDataReceived += new DataReceivedEventHandler(
                delegate(object sender, DataReceivedEventArgs e)
                
                    if (e.Data != null)
                    
                        lock (ExecutionContext)
                        
                            ExecutionContext.AppendOutput(e.Data);
                        
                    
                
            );

        Process.Start();
        Process.BeginOutputReadLine();
    


    public void AppendOutput(string str)
    
        // To do: Append strings to the _currentOutput within a period of time
        _currentOutput += str + Environment.NewLine;

        // Before doing the following
        SendOutput(_currentOutput);

        // Reset the variable
        _currentOutput = "";
    

SendOutput 方法通过 SignalR 将输出发送到 UI。

问题在于,当进程运行一个产生多个输出的命令时,它还会对 UI 进行多次 SignalR 调用,使其锁定。

首先,我使用 setTimeout javascript 方法解决了 UI 问题。它不再锁定,但由于一系列超时,它会延长输出的显示时间。

我认为处理这些输出的最佳方式是通过服务器端在一段时间内连接一系列输出,比如说 1 到 2 秒,然后再将它们发送到 UI。我倾向于使用 Timer 或 Task.Delay,但我无法清楚地构造它。

希望有人帮忙。

【问题讨论】:

【参考方案1】:

计时器应该会为您排序。这是一些可以做到这一点的代码(一个hack,因为我没有你的基类)。这在控制台中运行,因此显然您将不得不做一些工作,例如删除对控制台的调用。调用就在那里,因此您可以在控制台应用程序中对其进行测试。此处的 SendOutput 方法只是写入控制台,但您只需将其删除,它就会调用您的 signalR 客户端。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Timers;

namespace bufferOutput

    public class Processor
    
        private object bufferLock = new object();
        private StringBuilder sb = new StringBuilder();
        private Timer updateTime = new Timer(250);//fire every quarter second: a reasonable time for a web page to update

        public void Execute(string fileName, string args)
        
            var process = new Process  StartInfo =  FileName = fileName, Arguments = args  ;
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.RedirectStandardError = true;
            process.EnableRaisingEvents = true;
            process.Exited += ProcessExited;
            process.OutputDataReceived += AppendOutput;
            process.Start();
            updateTime.Elapsed += updateTime_Elapsed;
            process.BeginOutputReadLine();
            updateTime.Start();
            Console.Title = "Buffered data: Press the Enter key to exit the program.";
            Console.ReadLine();
            ProcessExited(this, EventArgs.Empty);
            Console.ReadLine();
        

        void updateTime_Elapsed(object sender, ElapsedEventArgs e)
        
            string output = "";
            lock(bufferLock)
            
                output = sb.ToString();
                if (output != "")//if there was anything in the strng builder, clear it
                    sb = new StringBuilder();
            
            if (output != "")
                SendOutput(output);
        

        public void ProcessExited(object sender, EventArgs e)
        
            updateTime.Stop();
            Console.Title = "writing out buffer: Press the Enter key to exit the program.";
            updateTime_Elapsed(this, null);
            SendOutput("Done");
        

        public void AppendOutput(object sender, DataReceivedEventArgs e)
        
            if (e.Data != null)
            
                lock (bufferLock)
                    sb.AppendLine(e.Data);
            
        

        public void SendOutput(string output)//placeholder for the client side
        
            Console.WriteLine(output);
        
    

看起来你正在将你的类锁定在进程的输出数据接收事件处理程序中,这可能不是最好的主意。

【讨论】:

感谢凯尔的努力。我现在正在使用计时器,但我仍在检查您的示例以进行比较。我可以知道为什么我的代码中的当前锁定不是一件好事吗? 看起来你的类被称为 ExecutionContext 所以你有效地锁定了类型。如果它是公共类型,这可能是一个问题,因为任何东西都可能锁定它并导致死锁。您通常希望锁定线程安全类中的私有对象以防止这种死锁 我用秒表代替,但逻辑是一样的。再次感谢凯尔。

以上是关于多次 SignalR 调用使 UI 没有响应的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 SignalR 获得延迟响应(控制器调用后台服务)?

响应发送到客户端后在 Django 中执行代码

RxJava 和 Realm 请求使应用程序跳帧

强制停止会使应用程序的接收器不被调用?

从 OData 响应将默认值绑定到 Combox

如何在材质 ui 上使用过渡使模态居中并使其响应?