2

我的应用程序中有一些量规。有时,当 UI 很忙时,我的仪表线程会停止等待更新一些仪表。在这种情况下,我想干脆放弃我的计划并尝试在下一次投票时更新仪表。我目前使用 Control.Invoke 从我的数据轮询线程移动到 UI。我不想使用 BeginInvoke,因为我不想浪费宝贵的 UI 时间来更新仪表,因为只有最终值才是重要的。如果我不能在 40 毫秒内进入 UI 线程,是否有其他方法可以在 UI 线程上调用代码以提前退出?Invoke 方法中的代码是必需的,还是有其他方法可以在主线程上调用方法?

4

4 回答 4

3

没有可用的超时选项。一种选择是使用BeginInvoke,但前提是前一条消息已被处理。这将需要线程同步,但可以类似于以下方式编写:

// using
object syncObj = new object();
bool operationPending = false;

while (operation)
{
   // ... Do work

   // Update, but only if there isn't a pending update
   lock(syncObj)
   {
       if (!operationPending)
       {
           operationPending = true;
           control.BeginInvoke(new Action( () =>
           {
               // Update gauges

               lock(syncObj)
                  operationPending = false;
           }));
       }
   }
}

// Update at the end (since you're last update might have been skipped)
control.Invoke(new Action(UpdateGuagesCompleted));

虽然这不会超时,但它会阻止您将 UI 事件“淹没”到主线程上,因为一次只会处理一个操作。


编辑:正如Yaur 提到的,这种方法也可以在不通过Interlocked锁定的情况下完成:

while (operation)
{
    int pendingOperations = 0;
    // ... Do work

    // Update, but only if there isn't a pending update
    if (0 == Interlocked.CompareExchange(ref pendingOperations, 1, 0))
    {
        control.BeginInvoke(new Action( () =>
        {   
            // Update gauges

            // Restore, so the next UI update can occur
            Interlocked.Decrement(ref pendingOperations);
        }));
    }       
}

// Update at the end (since you're last update might have been skipped)
control.Invoke(new Action(UpdateGuagesCompleted));
于 2013-11-07T22:26:15.690 回答
2

没有对添加超时的固有支持Control.InvokeBeginInvoke但是,您可以通过超时检查来模拟这个。

static void Invoke(this Control control, TimeSpan timeout, MethodInvoker callback)
{
    if (!control.InvokeRequired) {
        callback();
        return;
    }

    using (ManualResetEvent mre = new ManualResetEvent(initialState: false)) {
        bool cancelled = false;
        MethodInvoker wrapped = () => {
            mre.Set();
            if (!cancelled) {
                callback();
            }
        };

        control.BeginInvoke(wrapped);
        if (!mre.WaitOne(timeout)) {
            cancelled = true;
        }
    }
}

此方法将相当准确地模拟Control.Invoke超时。

于 2013-11-07T22:19:16.517 回答
0

如果您认为您可能会在很短的时间内完成大量更新,其中每次更新都会覆盖其他更新,那么更好的解决方案更有可能只是创建一个每隔一段时间触发并更新UI 基于您的工作人员确定的应执行的操作。创建一个表示用于更新 UI 的数据的实例字段,让工作人员在需要更新时设置它,然后让计时器在计时时使用存储的数据更新 UI。如果工作人员碰巧在滴答事件之间更改了 10 次变量,那么 UI 就更不明智了。

于 2013-11-07T22:17:24.577 回答
0

仅将最新值存储到变量中,然后处理 Application.Idle 事件如何?

据我了解,Application.Idle 在 UI 线程上的应用程序消息泵空闲时触发,从而确保 UI 已准备好更新您的仪表

于 2013-11-07T22:39:18.690 回答