【问题标题】:Several SignalR calls makes the UI not responding多次 SignalR 调用使 UI 没有响应
【发布时间】: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,但我无法清楚地构造它。

希望有人帮忙。

【问题讨论】:

    标签: c# multithreading user-interface signalr


    【解决方案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 所以你有效地锁定了类型。如果它是公共类型,这可能是一个问题,因为任何东西都可能锁定它并导致死锁。您通常希望锁定线程安全类中的私有对象以防止这种死锁
    • 我用秒表代替,但逻辑是一样的。再次感谢凯尔。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-02
    相关资源
    最近更新 更多