Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading 读取控制台应用程序的输出和WPF绘制异步_Multithreading_Wpftoolkit - Fatal编程技术网

Multithreading 读取控制台应用程序的输出和WPF绘制异步

Multithreading 读取控制台应用程序的输出和WPF绘制异步,multithreading,wpftoolkit,Multithreading,Wpftoolkit,我有一个控制台应用程序,每1秒输出大约160行信息 数据输出是可用于在图形上绘图的点 在我的WPF应用程序中,我已经成功地将其连接起来,并且正在绘制控制台应用程序的数据输出,但是,在大约500个数据点之后,我看到应用程序和UI线程锁定的速度明显减慢 我假设这是由于我使用的异步操作造成的: BackgroundWorker worker = new BackgroundWorker(); worker.DoWork += delegate(object s, DoWorkEventArgs ar

我有一个控制台应用程序,每1秒输出大约160行信息

数据输出是可用于在图形上绘图的点

在我的WPF应用程序中,我已经成功地将其连接起来,并且正在绘制控制台应用程序的数据输出,但是,在大约500个数据点之后,我看到应用程序和UI线程锁定的速度明显减慢

我假设这是由于我使用的异步操作造成的:

BackgroundWorker worker = new BackgroundWorker();

worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
    _process = new Process();
    _process.StartInfo.FileName = "consoleApp.exe";
    _process.StartInfo.UseShellExecute = false;
    _process.StartInfo.RedirectStandardOutput = true;
    _process.StartInfo.CreateNoWindow = true;
    _process.EnableRaisingEvents = true;
    _process.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
    _process.Start();
    _process.BeginOutputReadLine();
    _watch.Start();
};
worker.RunWorkerAsync();
以及负责分析和绘制数据的处理程序:

private void SortOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
    if (!String.IsNullOrEmpty(outLine.Data))
    {
            var xGroup = Regex.Match(outLine.Data, "x: ?([-0-9]*)").Groups[1];
            int x = int.Parse(xGroup.Value);

            var yGroup = Regex.Match(outLine.Data, "y: ?([-0-9]*)").Groups[1];
            int y = int.Parse(yGroup.Value);

            var zGroup = Regex.Match(outLine.Data, "z: ?([-0-9]*)").Groups[1];
            int z = int.Parse(zGroup.Value);


            Reading reading = new Reading()
            {
                Time = _watch.Elapsed.TotalMilliseconds,
                X = x,
                Y = y,
                Z = z
            };


            Dispatcher.Invoke(new Action(() =>
            {
                _readings.Enqueue(reading);
                _dataPointsCount++;

            }), System.Windows.Threading.DispatcherPriority.Normal);                    
    }
}
\u读数
是中定义的自定义
可观察设备
。我修改了它,一次只能有50个项目在队列中。因此,如果正在添加一个新项目且队列计数>=50,则在
排队()之前调用
Dequeue()


有什么方法可以提高性能,还是因为console应用程序的输出量太大而注定要失败?

从这里我可以看出,这是怎么回事:

  • IU线程启动后台工作程序来启动控制台应用程序
  • 它重定向控制台的输出,并使用UI线程上的处理程序对其进行处理
  • 然后UI线程上的处理程序调用Dispatcher.Invoke每秒160次,以更新同一线程上的队列对象
  • 50次调用后,队列开始阻塞,同时UI将项目移出队列
  • 问题似乎是:

    让UI线程处理来自控制台和队列的原始输出以及对图形的更新

    此外,当用户界面后面的数据项超过50个时,排队和退队之间的阻塞可能会导致级联故障。(我看不到足够的代码来确定这一点)

    决议:

  • 启动另一个后台线程来管理控制台应用程序中的数据
  • 新线程应该:创建队列;处理OutputDataReceived事件;并启动控制台应用程序进程
  • 事件处理程序不应使用Dispatcher.Invoke更新队列。应使用直接线程安全调用
  • 在更新UI时,队列确实需要是非阻塞的,但是我没有足够的信息来说明如何实现它
  • 希望这有帮助
    -克里斯

    从这里我可以看出,事情是这样的:

  • IU线程启动后台工作程序来启动控制台应用程序
  • 它重定向控制台的输出,并使用UI线程上的处理程序对其进行处理
  • 然后UI线程上的处理程序调用Dispatcher.Invoke每秒160次,以更新同一线程上的队列对象
  • 50次调用后,队列开始阻塞,同时UI将项目移出队列
  • 问题似乎是:

    让UI线程处理来自控制台和队列的原始输出以及对图形的更新

    此外,当用户界面后面的数据项超过50个时,排队和退队之间的阻塞可能会导致级联故障。(我看不到足够的代码来确定这一点)

    决议:

  • 启动另一个后台线程来管理控制台应用程序中的数据
  • 新线程应该:创建队列;处理OutputDataReceived事件;并启动控制台应用程序进程
  • 事件处理程序不应使用Dispatcher.Invoke更新队列。应使用直接线程安全调用
  • 在更新UI时,队列确实需要是非阻塞的,但是我没有足够的信息来说明如何实现它
  • 希望这有帮助
    -Chris

    我怀疑UI线程上出现了线程饥饿问题,因为您的后台线程正在封送对可观察集合的调用,这可能会迫使每次都重新创建底层CollectionView。这可能是一个相当昂贵的操作

    根据您如何配置XAML也是一个问题。单是测量/布局的改变就可能让你丧命。我可以想象,以数据输入的速度,UI没有机会正确地评估底层数据发生了什么

    我建议不要直接将视图绑定到队列。与您建议的使用可观察队列不同,请考虑:

  • 使用将内容限制在50项的常规队列。不要担心UI线程上发生NotifyCollectionChanged事件。您也不必将每个项目封送到UI线程

  • 在ViewModel中公开将队列作为其集合的CollectionViewSource对象

  • 在UI上使用计时器线程手动强制刷新CollectionViewSource。从每秒一次开始,并缩短间隔,以查看XAML和机器可以处理什么。通过这种方式,您可以控制CollectionView的创建和销毁时间


  • 我怀疑UI线程上发生了线程饥饿问题,因为您的后台线程正在封送对可观察集合的调用,这可能会迫使每次都重新创建底层CollectionView。这可能是一个相当昂贵的操作

    根据您如何配置XAML也是一个问题。单是测量/布局的改变就可能让你丧命。我可以想象,以数据输入的速度,UI没有机会正确地评估底层数据发生了什么

    我建议不要直接将视图绑定到队列。与您建议的使用可观察队列不同,请考虑:

  • 使用将内容限制在50项的常规队列。不要担心UI线程上发生NotifyCollectionChanged事件。您也不必将每个项目封送到UI线程

  • 在视图中公开CollectionViewSource对象
    // Standard warnings apply: not tested, no exception handling, etc.
    
         var locker = new object();
         var que = new ConcurrentQueue<string>();
         var worker = new BackgroundWorker();
         var proc = new Process();
    
         proc.StartInfo.FileName = "consoleApp.exe";
         proc.StartInfo.UseShellExecute = false;
         proc.StartInfo.RedirectStandardOutput = true;
         proc.StartInfo.CreateNoWindow = true;
         proc.EnableRaisingEvents = true;
    
         proc.OutputDataReceived +=
            (p, a) =>
            {
               que.Enqueue(a.Data);
               Monitor.Pulse(locker);
            };
    
         worker.DoWork +=
            (s, e) =>
            {
               var watch = Stopwatch.StartNew();
               while (!e.Cancel)
               {
                  while (que.Count > 0)
                  {
                     string data;
                     if (que.TryDequeue(out data))
                     {
                        if (!String.IsNullOrEmpty(data))
                        {
                           var xGroup = Regex.Match(data, "x: ?([-0-9]*)").Groups[1];
                           int x = int.Parse(xGroup.Value);
    
                           var yGroup = Regex.Match(data, "y: ?([-0-9]*)").Groups[1];
                           int y = int.Parse(yGroup.Value);
    
                           var zGroup = Regex.Match(data, "z: ?([-0-9]*)").Groups[1];
                           int z = int.Parse(zGroup.Value);
    
                           var reading = new Reading()
                           {
                              Time = watch.Elapsed.TotalMilliseconds,
                              X = x,
                              Y = y,
                              Z = z
                           };
    
                           worker.ReportProgress(0, reading);
                        }
                     }
                     else break;
                  }
                  // wait for data or timeout and check if the worker is cancelled.
                  Monitor.Wait(locker, 50);
               }
            };
    
         worker.ProgressChanged +=
            (s, e) =>
            {
               var reading = (Reading)e.UserState;
               // We are on the UI Thread....do something with the new reading...
            };
    
         // start everybody.....
         worker.RunWorkerAsync();
         proc.Start();
         proc.BeginOutputReadLine();
    
    List<Reading> _Readings = new List<Reading>();
    DateTime _LastUpdateTime = DateTime.Now;
    TimeSpan _UpdateInterval = new TimeSpan(0,0,0,0,1*1000); // Update every 1 second
    
    private void SortOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
    {
        if (!String.IsNullOrEmpty(outLine.Data))
        {
                var xGroup = Regex.Match(outLine.Data, "x: ?([-0-9]*)").Groups[1];
                int x = int.Parse(xGroup.Value);
    
                var yGroup = Regex.Match(outLine.Data, "y: ?([-0-9]*)").Groups[1];
                int y = int.Parse(yGroup.Value);
    
                var zGroup = Regex.Match(outLine.Data, "z: ?([-0-9]*)").Groups[1];
                int z = int.Parse(zGroup.Value);
    
    
                Reading reading = new Reading()
                {
                    Time = _watch.Elapsed.TotalMilliseconds,
                    X = x,
                    Y = y,
                    Z = z
                };
    
                // create a batch of readings until it is time to send it to the UI
                // via ONE window message and not hundreds per second. 
                _Readings.Add(reading);
    
                DateTime current = DateTime.Now;
                if( current -_LastUpdateTime > _UpdateInterval )  // update ui every second 
                {
                     _LastUpdateTime  = current;
                     List<Reading> copy = _Readings;  // Get current buffer and make it invisible to other threads by creating a new list. 
                    // Since this is the only thread that does write to it this is a safe operation.
    
    
                     _Readings = new List<Reading>(); // publish a new empty list 
    
                     Dispatcher.Invoke(new Action(() =>
                     {
                        // This is called as part of a Window message in the main UI thread
                        // once per second now and not every 6 ms. Now we can upate the ui
                        // with a batch of 160 points at once. 
                        // A further optimization would be to disable drawing events 
                        // while we add the points to the control and enable it after
                        // the loop
                        foreach(Reading reading in copy)
                        {
                            _readings.Enqueue(reading);
                           _dataPointsCount++;
                        }
    
                     }),
                     System.Windows.Threading.DispatcherPriority.Normal);                    
                }
        }
    }