【问题标题】:Freezing GUI Elements冻结 GUI 元素
【发布时间】:2014-04-09 16:10:48
【问题描述】:

我是新来的,总是在这里找到有用的线程,但在这种情况下不是。我的程序使用线程时遇到问题。特别是,我的线程在后台做一些图像模式,一切似乎都很好。 它还可以在未定义的时间(有时是 15 秒,有时是几分钟)内工作,没有任何异常或冻结或其他任何情况。但随后我的 GUI 冻结,而不是我的整个 GUI,只是从线程更新的 GUI 部分。另外两个图片框工作正常(流式传输视频),但其余的不工作。 试图在它所在的位置停止线程是可行的,但是从那里启动它会使我的程序崩溃。 没有抛出任何异常。如有必要,每个 GUI 元素都会通过 Invoke() 进行更新。我只处理图片的副本以避免任何锁定模式或其他任何事情。我也尝试让 UI 做它需要做的事情(DoEvents()) 一些想法? 代码:

namespace My.Name.Space
{
    public class MyThread : MyThreadBase
    {

         public MyThread ( getting object s from the form for updating UI elements)
         {
              //referencing objects
              Stopwatch.Start();
         }


       //example Method for UI updating
       private void UpdateRobot1Box(int angle, int x, int y)   
       {
           if (_rob1.InvokeRequired)
           {
               _rob1.Invoke(new Action(() => _rob1.Clear()));
               _rob1.Invoke(new Action(() => _rob1.Text = angle.ToString() + "°, X:" + x.ToString() + ", Y:" + y.ToString()));
           }
           else
           {
               _rob1.Clear();
               _rob1.Text = angle.ToString() + "°, X:" + x.ToString() + ", Y:" + y.ToString();
           }
       }

          protected override void Loop(CancellationToken token)
          {
               while(!token.IsCancellationRequested)
               {
                     if( PictureBox != null && Stopwatch.ElapsedMilliseconds >= tick)
                     {
                          //DoWork
                          Application.DoEvents();
                     }
                     else
                     {
                          Thread.Sleep(1);
                     }
                 }
             }
         }
     }
 }

编辑 1:

MyThreadBase:

namespace My.Name.Space
{
    public abstract class MyThreadBase : DisposableBase//just some simple gc stuff
    {
        private CancellationTokenSource _cancellationTokenSource;

        public bool IsAlive
        {
            get { return _cancellationTokenSource != null; }
        }

        public event Action<Object, Exception> UnhandledException;

        public void Start()
        {
            if (_cancellationTokenSource != null)
                return;
            lock (this)
            {
                if (_cancellationTokenSource != null)
                    return;

                _cancellationTokenSource = new CancellationTokenSource();

                var thread = new Thread(RunLoop) {Name = GetType().Name};
                thread.Start();
            }
        }

        public void Stop()
        {
            if (_cancellationTokenSource == null)
                return;
            lock (this)
            {
                if (_cancellationTokenSource == null)
                    return;

                _cancellationTokenSource.Cancel();
            }
        }

        public void Join()
        {
            while (IsAlive) Thread.Sleep(1);
        }

        private void RunLoop()
        {
            try
            {
                CancellationToken token = _cancellationTokenSource.Token;
                Loop(token);
            }
            catch (OperationCanceledException)
            {
                throw;
            }
            catch (Exception exception)
            {
                OnException(exception);
            }
            finally
            {
                lock (this)
                {
                    CancellationTokenSource cancellationTokenSource = _cancellationTokenSource;
                    _cancellationTokenSource = null;
                    cancellationTokenSource.Dispose();
                }
            }
        }

        protected abstract void Loop(CancellationToken token);

        protected virtual void OnException(Exception exception)
        {
            Trace.TraceError("{0} - Exception: {1}", GetType(), exception.Message);
            Trace.TraceError(exception.StackTrace);

            OnUnhandledException(exception);
        }

        protected virtual void OnUnhandledException(Exception exception)
        {
            if (UnhandledException != null)
                UnhandledException(this, exception);
        }

        protected override void DisposeOverride()
        {
            Stop();
        }
    }

UpdateRobot1Box 在线程内的 switch-case 构造中被调用。我有一个小顺序,我通过自己创建的对象列表来决定在我的文本框中写什么。

【问题讨论】:

  • Thread.Sleep 可能无济于事
  • 我认为需要发布更多代码。 MyThreadBase 是什么,UpdateRobot1Box 怎么称呼?
  • @DustinDavis 执行Thread.Sleep(1) 允许线程在while 循环中不会不断地最大化CPU/核心。只需 1 毫秒就有很大帮助。
  • Sleep(1),(无意义,向下舍入为 0)和 'Application.DoEvents()',(尤其是来自非 GUI 线程)。你注定要失败。
  • 应避免使用此类原语(线程类)进行异步编程,您应该考虑使用 TPL(Task)或 Rx(响应式扩展)。对我来说最大的问题是使用 Application.DoEvents 来保持 UI 响应 - 这应该避免并且是代码异味的标志。

标签: c# multithreading user-interface freeze


【解决方案1】:

在主窗体类中创建一个方法来执行 UI 更新操作:

private void AsyncFormUpdate(Action action)
{
        if (this.InvokeRequired)
        {
            this.Invoke(action, null);
        }
        else
        {
            action();
        }
}

将其放置在适当的位置后,您就可以确定 InvokeRequired 可以正常运行,并且代码被更好地封装。

接下来简单点。使用带有反馈的异步调用委托向 UI 报告角度和坐标更改,您将在其中实际调用 AsyncFormUpdate 方法。 这里展示了一个很好的例子:

http://www.csharp-examples.net/asynchronous-method-progress/

他们在那里更新进度,在那里你将更新角度和 X/Y 坐标。

【讨论】:

    猜你喜欢
    • 2013-01-16
    • 2020-08-06
    • 2015-04-08
    • 2015-05-09
    • 1970-01-01
    • 1970-01-01
    • 2012-06-26
    • 2015-03-05
    相关资源
    最近更新 更多