c# - 使用调度程序仅运行一次代码

标签 c# dispatcher ref

我有一个简单的模式,只运行一次代码。它主要用于更新 UI 上的某些内容,而它可能在后台经常更改。

private bool _updating;
private void UpdateSomething()
{
    if (!_updating)
    {
        _updating = true;

        Application.Current.Dispatcher.BeginInvoke(new Action(() =>
            {
                _updating = false;
                DoSomething();
            }), DispatcherPriority.Background);
    }
}

我更愿意将样板代码放在一个简单的方法中:

public static void RunOnce(Action action, ref bool guard)
{
    if (!guard)
    {
        guard = true;

        Application.Current.Dispatcher.BeginInvoke(new Action(() =>
            {
                guard = false;
                action();
            }), DispatcherPriority.Background);
    }
}

这样调用它:

void UpdateSomething()
{
    RunOnce(DoSomething, ref _updating);
}

但是,这不起作用,因为匿名方法中不能有 ref 参数。 有没有解决方法,例如固定 ref 参数并在执行方法时释放它?

最佳答案

你可以这样做:

public static void RunOnce(Action action, ref RunOnceToken token)
{
    if (token == null || token.IsCompleted)
    {
        token = new RunOnceToken(
            Application.Current.Dispatcher.BeginInvoke(
                action,
                DispatcherPriority.Background));
    }
}

public sealed class RunOnceToken : IDisposable
{
    private DispatcherOperation _operation;

    public RunOnceToken(DispatcherOperation operation)
    {
        if (operation != null &&
            operation.Status != DispatcherOperationStatus.Completed &&
            operation.Status != DispatcherOperationStatus.Aborted)
        {
            _operation = operation;
            _operation.Completed += OnCompletedOrAborted;
            _operation.Aborted += OnCompletedOrAborted;
        }
    }

    private void OnCompletedOrAborted(object sender, EventArgs e)
    {
        this.Dispose();
    }

    public bool IsCompleted
    {
        get { return _operation == null; }
    }

    public void Dispose()
    {
        var operation = _operation;
        if (operation == null)
            return;

        _operation = null;

        operation.Completed -= OnCompletedOrAborted;
        operation.Aborted -= OnCompletedOrAborted;
    }
}

您的示例用法将更改为:

private RunOnceToken _updateToken;

private void UpdateSomething()
{
    RunOnce(DoSomething, ref _updateToken);
}

如果您从不清除 token 的副本,这并不重要,因为包装的 DispatcherOperation 在完成后会被清除,以避免泄漏 action 或其任何值捕获。

如果不是很明显的话,这些都不是并发安全的;我假设以上所有内容只能从 UI 线程访问。

一个有用的增强功能可能是向 RunOnce 添加可选的 DispatcherPriority 参数,以便您可以控制用于调度操作的优先级(如果已安排的优先级较低,则可能会取消已安排的操作)。

关于c# - 使用调度程序仅运行一次代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26385408/

相关文章:

c# - 用空格替换所有非单词字符

c# - InvalidOperationException:调度程序处理已暂停,但消息仍在处理中

c# wpf dispatcher.beginInvoke 卡住

dispatcher - ExecutorCoroutineDispatcher 和 CoroutineDispatcher 的区别

javascript - React 中的 ScrollIntoView 没有引用?

c# - 通过名称访问静态变量

c# - 指针是不安全的,但我不明白 ref

c# - 延迟删除 aws s3 中的单个对象

c# - 如何发送多部分响应?

c# - 'OpenFileDialog' 第一次不取值