Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何重复执行一个方法而不影响GUI,直到应用程序退出?C#WPF_C#_Wpf_Multithreading_Timer_Backgroundworker - Fatal编程技术网

如何重复执行一个方法而不影响GUI,直到应用程序退出?C#WPF

如何重复执行一个方法而不影响GUI,直到应用程序退出?C#WPF,c#,wpf,multithreading,timer,backgroundworker,C#,Wpf,Multithreading,Timer,Backgroundworker,我需要每隔60秒执行一个方法,直到应用程序退出,而不需要使用C#WPF减慢GUI的速度 每60秒执行一次的方法检查数据库表中的布尔值。如果该值为true,则应用程序将继续运行,如果该值为false,则应用程序将退出 我见过一些帖子使用System.Threading.Timer、后台工作人员和任务。但是,我不知道使用哪一个是最好的选择,这样GUI仍然是交互式的 如果您有任何建议,我们将不胜感激。我们这里没有实时操作系统,因此讨论计时器的精确性是没有意义的——它们从来都不是绝对精确的。但是,如果我

我需要每隔60秒执行一个方法,直到应用程序退出,而不需要使用C#WPF减慢GUI的速度

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

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


如果您有任何建议,我们将不胜感激。

我们这里没有实时操作系统,因此讨论计时器的精确性是没有意义的——它们从来都不是绝对精确的。但是,如果我们谈论的是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
        });
公共
主窗口()
{
初始化组件();
var cts=新的CancellationTokenSource();
var task=CheckToClose(cts.Token);
关闭+=委托{cts.Cancel();};
}
异步的
任务检查关闭(取消令牌ct)
{
而(!ct.iscancellationrequest)
{
尝试{wait Task.Delay(TimeSpan.FromSeconds(60),ct);}catch(TaskCanceledException){return;}
var keepRunning=await ShouldKeepRunning(ct);
如果(!正在修剪)
{
Close();
返回;
}
}
}
任务应保持修剪(取消令牌ct)=>
//在这里执行线程池任务很重要,这样就可以使用数据库了
//不会阻止用户界面
任务。运行(
代表
{
//在这里,您可以询问数据库是否继续运行
//如果使用DB可能太慢,
//然后将CancellationToken考虑在内
});

以下是使用
调度计时器和
异步
/
等待
的示例。这是大多数WPF应用程序的首选计时器。在某些情况下,您可能需要其他计时器,但此计时器将处理大多数WPF任务

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

async
/
await
调用允许数据库操作(此处通过调用
Task.Delay
)不阻塞UI。如果您使用的是实体框架,则应使用适当的异步DB操作替换
Task.Delay
,如
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);
        }
    }
}
使用系统;
使用系统诊断;
使用System.Threading.Tasks;
使用System.Windows;
使用System.Windows.Threading;
命名空间异步测试
{
公共部分类BetterWindow:窗口
{
公共图书馆()
{
初始化组件();
var timer=newdispatchermer();
timer.Interval=从秒开始的时间跨度(10);
timer.Tick+=定时器_Tick;
timer.Start();
}
专用异步无效计时器(对象发送方、事件参数)
{
//“async void”通常不受欢迎,但对于事件处理程序来说是可以接受的。
if(wait ShouldExit())
{
Close();
}
}
私有异步任务ShouldExit()
{
Debug.WriteLine(“检查数据库”);
//模拟长DB操作
等待任务延迟(时间跨度从秒(5));
返回chkAllowClose.IsChecked.getValuerDefault(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>


在WPF中,如果可能的话,您应该支持使用。此计时器允许您不必担心线程问题。DispatchTimer不保证按间隔计时器运行,因此我读取@BradleyuffnerWindows中的一个计时器保证按精确的间隔运行。这需要一个“实时操作系统”,而Windows则不是。在我遇到的99.99%的情况下,它们只差几毫秒。没有人会注意到。这只是一般的免责声明。如果检查数据库是一项耗时的操作,那么在Dispatcher的Tick处理程序中执行该操作可能会阻塞UI。然后,您可以使处理程序异步并等待检查db的任务,或者使用其他一些计时器类,例如在线程池线程上运行的System.Threading.timer。请注意,您必须使用应用程序的调度程序才能访问UI。@Rhysf93任何计时器在准确时间运行的可能性微乎其微。调度程序将在“关闭”到所需时间时触发它们。但这是一个cpu周期的问题。一个有趣的解决方案。您不需要使用
任务。但运行
可以避免阻塞UI。只需在db操作上使用
wait
,就可以避免阻塞UI。不过,另一个问题是,这将在应用程序的整个生命周期中消耗线程池中的一个线程(假设您在调用
CheckToClose
时使用
wait
修复编译错误)。简单地使用wait将使您保持在UI线程中,任何对DB的阻塞调用都将因此阻塞它。这是错误的。。。
await
的整个要点是在不阻塞UI线程的情况下等待
async
操作的结果。这就是它的设计目的。为了避免需要