在应用程序退出之前,如何重复执行方法而不影响GUI? C#WPF

时间:2018-06-21 11:40:44

标签: c# wpf multithreading timer backgroundworker

我需要每60秒执行一次方法,直到应用程序退出,而又不使用C#WPF降低GUI的速度。

每60秒执行一次的方法将检查数据库表中的布尔值。如果值为true,则应用程序继续运行;如果值为false,则应用程序退出。

我看过一些使用System.Threading.Timers,后台工作人员和任务的帖子。但是我不知道哪个是最好的选择,这样GUI仍然是交互式的

任何建议将不胜感激

2 个答案:

答案 0 :(得分:0)

以下是使用DispatchTimerasync / await的示例。这是大多数 WPF应用程序的首选计时器。在某些情况下,您可能需要其他计时器,但是该计时器将处理大多数WPF任务。

DispatchTimer允许代码在UI线程上运行,因此您不必担心手动分派跨线程操作。

通过async / await调用,数据库操作(此处与对Task.Delay的调用类似)不会阻塞UI。您应该使用适当的异步DB操作替换Task.Delay,例如,如果使用的是Entity Framework,请使用DbContext.SomeDbSet.FirstOrDefaultAsync()

XAML仅显示一个复选框,该复选框允许应用程序在选中时退出,并带有动画标签,以证明UI线程未被阻止。


BetterWindow.xaml.cs:

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;

namespace asyncTest
{
    public partial class BetterWindow : Window
    {
        public BetterWindow()
        {
            InitializeComponent();

            var timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromSeconds(10);
            timer.Tick += Timer_Tick;
            timer.Start();
        }

        private async void Timer_Tick(object sender, EventArgs e)
        {
            // "async void" is generally frowned upon, but it is acceptable for event handlers.
            if (await ShouldExit())
            {
                Close();
            }
        }

        private async Task<bool> ShouldExit()
        {
            Debug.WriteLine("Checking the DB");
            //Simulate a long DB operation
            await Task.Delay(TimeSpan.FromSeconds(5));
            return chkAllowClose.IsChecked.GetValueOrDefault(false);
        }
    }
}

BetterWindow.xaml:

<Window x:Class="asyncTest.BetterWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:asyncTest"
        mc:Ignorable="d"
        Title="BetterWindow" Height="450" Width="800">
    <DockPanel>
        <CheckBox Content="Allow Close" Name="chkAllowClose" DockPanel.Dock="Top"></CheckBox>
        <Grid>
            <Label Content="The UI isn't locked!" RenderTransformOrigin="0.5, 0.5" Width="200" Height="200">
                <Label.RenderTransform>
                    <RotateTransform x:Name="noFreeze" />
                    </Label.RenderTransform>
                <Label.Triggers>
                    <EventTrigger RoutedEvent="Loaded">
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation
                                        Storyboard.TargetProperty="(Label.RenderTransform).(RotateTransform.Angle)"
                                        To="-360" Duration="0:0:10" RepeatBehavior="Forever" />
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                </Label.Triggers>
            </Label>
        </Grid>
    </DockPanel>
</Window>

答案 1 :(得分:-1)

我们不在这里使用实时操作系统,因此讨论计时器的精确性没有意义-它们永远都不是绝对精确的。但是,如果我们谈论的是60秒这样的时间粒度,那么无论如何都没关系。

因此可以选择一个计时器 ,但我更喜欢另一种方式:

public
MainWindow()
{
    InitializeComponent();

    var cts = new CancellationTokenSource();
    var task = CheckToClose(cts.Token);
    Closing += delegate { cts.Cancel(); };
}

async
Task CheckToClose(CancellationToken ct)
{
    while (!ct.IsCancellationRequested)
    {
        try { await Task.Delay(TimeSpan.FromSeconds(60), ct); } catch (TaskCanceledException) { return; }
        var keepRunning = await ShouldKeepRunning(ct);
        if (!keepRunning)
        {
            Close();
            return;
        }
    }
}

Task<bool> ShouldKeepRunning(CancellationToken ct) =>
    // it's important here to take a thread pool task here so that working with the DB
    // doesn't block the UI
    Task.Run<bool>(
        delegate
        {
            // here you ask the DB whether you keep running
            // if working with DB is potentially too slow,
            // then take the CancellationToken into account
        });