WinForms中的Control.Invoke(timeout)之类的东西?

时间:2013-11-07 22:10:56

标签: c# .net multithreading winforms invoke

我的应用程序中有一些指标。有时,当UI忙时,我的仪表线程会停止等待更新某些仪表。在那种情况下,我想简单地放弃我的计划,并尝试在下一次民意调查中更新指标。我目前使用Control.Invoke从我的数据轮询线程移动到UI。我不想使用BeginInvoke,因为我不想浪费宝贵的UI时间更新仪表,因为它只是重要的最终值。有没有其他方法可以在UI线程上调用代码,如果我无法在40ms内进入UI线程,我可以提前纾困?是否需要Invoke方法中的代码,或者是否有其他方法可以在主线程上调用方法?

4 个答案:

答案 0 :(得分: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 mentioned时,也可以通过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));

答案 1 :(得分:1)

Control.Invoke添加超时没有固有的支持。但是,您可以通过BeginInvoke和超时检查来模拟这一点。

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(callback);
        if (!mre.WaitOne(timeout)) {
            cancelled = true;
        }
    }
}

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

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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

我理解它的方式,当UI线程上的应用程序消息泵空闲时,会触发Application.Idle,从而确保UI准备好更新您的仪表