【问题标题】:Make Async function run completely in the background使 Async 函数完全在后台运行
【发布时间】:2017-03-08 23:03:24
【问题描述】:

我有一个计时器,它每 15 秒调用一次方法,该方法需要一些时间才能完成。 我已尽可能将其转换为async,但它在运行时仍会冻结 UI 近 1 秒,并且由于它每 15 秒运行一次,因此变得很烦人。

知道如何让这个async 方法完全脱离网格运行吗? 这是定时器方法:

public static DispatcherTimer UpdateList = new DispatcherTimer();
//GlobalVars.MainList = new List<string>();  //saved list from previous declaration

public MainFunction()
{
    this.InitializeComponent();
    UpdateList.Tick += UpdateList_Tick; ;
    UpdateList.Interval = new TimeSpan(0, 0, 0, 0, 15000);
    UpdateList.Start();
    //...
}
private async void UpdateList_Tick(object sender, object e)
    {   
    using (var client = new ImapClient())
            {
                using (var cancel = new CancellationTokenSource())
                {
                    await client.ConnectAsync("imap.gmail.com", 993, true, cancel.Token);
                    client.AuthenticationMechanisms.Remove("XOAUTH");

                    await client.AuthenticateAsync("email.com", "mail12345", cancel.Token);

                    var inbox = client.Inbox;
                    await inbox.OpenAsync(FolderAccess.ReadOnly, cancel.Token);

                    // let's try searching for some messages...
                    DateTime date = DateTime.Now;
                    DateTime mondayOfLastWeek = date.AddDays(-(int)date.DayOfWeek - 6);
                    var query = SearchQuery.DeliveredAfter(mondayOfLastWeek)
                        .And(SearchQuery.SubjectContains("search"))
                        .And(SearchQuery.All);
                    List<string> newList = new List<string>();
                    foreach (var uid in inbox.Search(query, cancel.Token))
                    {
                        var message = inbox.GetMessage(uid, cancel.Token);
                        string trimmedMSGEmtyLines = Regex.Replace(message.TextBody, @"^\s+$[\r\n]*", "", RegexOptions.Multiline);
                        newList.Add(message.Date.LocalDateTime + Environment.NewLine + trimmedMSGEmtyLines);
                    }
                    await client.DisconnectAsync(true, cancel.Token);
                    if (!GlobalVars.MainList.SequenceEqual(newList))
                    {
                        GlobalVars.MainList = newList;
                    }
                }
            }
}

更新: MailKitPortable.Text.EncodingMimeKit

【问题讨论】:

  • 是否有理由使用 BackgroundWorker 对您不起作用?
  • async 方法中有很多同步代码。对inbox.Search()Regex.Replace() 的调用都可能代价高昂,foreach 循环也无济于事。您可以尝试更改为 foreach (var uid in await Task.Run(() =&gt; inbox.Search(query, cancel.Token))) 作为第一次尝试改善延迟。除此之外,如果没有可靠地重现问题的良好 minimal reproducible example,就无法确定您需要修复什么。
  • @khargosh:或者可能是缺乏async/await 的经验。我已经使用BackgroundWorker 十五年了,但是一旦我们得到async/await,我就不再再使用BW了。与处理 BW 时相比,代码如此更容易阅读和编写(这在当时很棒,但现在看起来很摇摇晃晃)。
  • 另外,由于您已经在使用 async/await 并且希望以 15 秒的间隔执行代码,您可以考虑将其放入 while 循环并使用 @987654345 @ 作为你的计时器,而不是显式地创建一个计时器。
  • “上面的代码确实可以重现,但我不建议你测试它,因为它需要安装包” -- 请阅读minimal reproducible example,这样你就明白了什么这句话的意思。另请参阅How to Ask,尤其是底部链接的文章。 “需要安装包” 只是另一种表示代码不是 一个好的minimal reproducible example

标签: c# asynchronous timer background uwp


【解决方案1】:

async / await 在执行长时间运行的操作时很有用,这些操作与 UI 分离,同时在 UI 线程上运行。这正是您正在做的事情 - 但似乎您一开始就没有在 UI 线程上做生意。真的,您可以 await 事件处理程序的整个 内容,因为它与 UI 完全分离。所以它不是适合这项工作的工具。

BackgroundWorker 被建议,这将是一个很好的选择。我选择使用System.Threading.Timer 提供解决方案,因为与您现有的代码相比,没有太多需要更改的代码。如果添加任何更新 UI 的代码,则需要将其调用回调度程序线程。

所有这些异步方法都可能同步运行。我会删除async / await,然后选择同步方法,例如client.ConnectAsync() >> client.Connect()

public System.Threading.Timer UpdateTimer;

public MainFunction()
{
    this.InitializeComponent();
    UpdateTimer = new System.Threading.Timer(UpdateList_Tick);
    UpdateTimer.Change(0, 15000);
    //...
}

private async void UpdateList_Tick(object state)
{   
    using (var client = new ImapClient())
    {
        using (var cancel = new CancellationTokenSource())
        {
            await client.ConnectAsync("imap.gmail.com", 993, true, cancel.Token);
            client.AuthenticationMechanisms.Remove("XOAUTH");

            await client.AuthenticateAsync("email.com", "mail12345", cancel.Token);

            var inbox = client.Inbox;
            await inbox.OpenAsync(FolderAccess.ReadOnly, cancel.Token);

            // let's try searching for some messages...
            DateTime date = DateTime.Now;
            DateTime mondayOfLastWeek = date.AddDays(-(int)date.DayOfWeek - 6);
            var query = SearchQuery.DeliveredAfter(mondayOfLastWeek)
                .And(SearchQuery.SubjectContains("search"))
                .And(SearchQuery.All);
            List<string> newList = new List<string>();
            foreach (var uid in inbox.Search(query, cancel.Token))
            {
                var message = inbox.GetMessage(uid, cancel.Token);
                string trimmedMSGEmtyLines = Regex.Replace(message.TextBody, @"^\s+$[\r\n]*", "", RegexOptions.Multiline);
                newList.Add(message.Date.LocalDateTime + Environment.NewLine + trimmedMSGEmtyLines);
            }
            await client.DisconnectAsync(true, cancel.Token);
            if (!GlobalVars.MainList.SequenceEqual(newList))
            {
                GlobalVars.MainList = newList;
            }
        }
    }
}

【讨论】:

  • 我有一个关于调用 UI 的问题。我在 UWP 上。我想调用一个表格。 SettingsFlyout fly = new SettingsFlyout(); fly.Show();。然而,这并没有调用它,因为它在另一个线程上。我该怎么称呼它? .Invoke and .BeginInvoke 在此处不可用。
  • 诚然,我对 UWP 一无所知,但我找到了this question,其中包含一些可能对您有所帮助的信息。
猜你喜欢
  • 2020-08-29
  • 2019-09-05
  • 1970-01-01
  • 2015-10-29
  • 2019-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-03
相关资源
最近更新 更多