C# 未使用等待任务。延迟(x)(x>;0)使UI线程冻结

C# 未使用等待任务。延迟(x)(x>;0)使UI线程冻结,c#,wpf,multithreading,asynchronous,C#,Wpf,Multithreading,Asynchronous,我有一个函数,它可以强制guid计算ID(guid是ID的散列计算,所以只有通过2^32-1(是的,没错)可能的ID进行强制执行,才能逆转这个过程 我使用了wait Task.Delay(1);来“刷新”用户界面线程,这样一切看起来都很自然,但是这会大大降低进程的速度,但是如果我使用wait Task.Delay(0);整个应用程序将冻结,我将不知道搜索的当前进度(显示为进度条的2 I进度s) async public Task<Dictionary<string, stri

我有一个函数,它可以强制guid计算ID(guid是ID的散列计算,所以只有通过2^32-1(是的,没错)可能的ID进行强制执行,才能逆转这个过程

我使用了
wait Task.Delay(1);
来“刷新”用户界面线程,这样一切看起来都很自然,但是这会大大降低进程的速度,但是如果我使用
wait Task.Delay(0);
整个应用程序将冻结,我将不知道搜索的当前进度(显示为进度条的
2 I进度
s)

    async public Task<Dictionary<string, string>> GetSteamIds(Dictionary<string,string> GUIDs, IProgress<UInt32> progress, IProgress<double> progress2)
    {
        List<string> Progress = new List<string>();
        foreach(string GUID in GUIDs.Keys)
        {
            Progress.Add(GUID);
        }
        for (;Iteration <= UInt32.MaxValue;Iteration++)
        {
            await Task.Delay(0);
            string guid = CalculateGUID(MinAcc+Iteration);
            if(GUIDs.ContainsKey(guid))
            {
                GUIDs[guid] = Convert.ToString((Int64)(MinAcc + Iteration));
            }

            progress.Report(Iteration);
            progress2.Report((1.0 * Iteration / UInt32.MaxValue * 100));

            if(Progress.Count==0)
            {
                if (progress2 != null)
                {
                    progress2.Report(100);
                }
                break;
            }
        }

        return GUIDs;
    }
async public Task getstreamids(字典guid、IProgress progress、IProgress progress2)
{
列表进度=新列表();
foreach(GUIDs.Keys中的字符串GUID)
{
进度。添加(GUID);
}

对于(;迭代不要在UI线程上运行任务

你可以这样称呼它:

var myTask = Task.Run(() => GetSteamIds(...));
并在某处保留对
myTask
的引用,以便您可以测试
myTask.IsCompleted
以查看它何时完成

或者,您可以使用ContinueWith在任务完成时自动运行其他一些代码。例如:

var myTask = Task.Run(() => GetSteamIds(...)).ContinueWith(() => {
    //do something here now that the task is done
  }, TaskScheduler.FromCurrentSynchronizationContext());
TaskScheduler.FromCurrentSynchronizationContext()
用于确保延续代码在UI线程上运行(因为您可能希望使用结果更新UI)

使用这种用法,您的方法根本不需要是异步的(它不需要返回任务)


或者按照注释中的建议使用。

当您调用
等待任务时。延迟(1)
实际上会返回对UI线程的访问权限,因为问题可能不在您调用的方法中,而是在方法调用中


因此,您应该删除方法中的Task.Delay(),并向我们显示该方法的调用,以便我们可以进一步帮助您

您的
GetSteamIds
方法是完全同步的,并且受CPU限制,因此它应该具有同步签名:

public Dictionary<string, string> GetSteamIds(Dictionary<string,string> GUIDs, IProgress<UInt32> progress, IProgress<double> progress2);
如果您使用的是
Progress
,那么它将为您处理UI线程上的进度更新;不需要笨拙的
调度程序
控件。调用


在一个旁注下,像这样一个非常紧凑的CPU绑定循环,你可能发现你的进度报告本身会减慢你的UI并降低你的用户体验。在这种情况下,考虑使用我的“<代码> i进度< /COD>实现”来节制进度更新。

< P>添加一个方法实际上不是MA。将该方法设为异步,这意味着您可以在该方法中使用
wait
关键字。您已经编写了一个完全同步的CPU绑定方法,并将该方法标记为
async
,使其仍然只是一个同步的CPU绑定方法,并且可以从UI线程调用同步的CPU绑定方法正在初始化以阻止UI线程,从而阻止其他UI操作的发生

添加
等待任务。延迟(1)
使您的方法仍然在UI线程中执行其所有长时间运行的CPU限制工作,它只是偶尔停止一次以移动到行的末尾,允许其他操作运行。这不仅会降低您的工作速度,还意味着UI线程在绝大多数时间内无法执行其他UI操作因为这个过程占用了太多的时间

解决方案是根本不在UI线程中执行长时间运行的CPU绑定工作,而是将该工作转移到另一个线程


因为您的方法实际上不是异步的,所以应该从方法中删除
async
关键字(并相应地更改返回类型)。当您调用此方法时,如果碰巧需要从UI线程执行此操作,只需将其包装在对
任务的调用中。运行
,将其卸载到线程池线程。如果该UI操作需要在完成结果后执行工作,则该UI操作可以
等待
任务的结果。运行
,然后method应该被标记为
async
,因为它确实是异步工作的。

它看起来不像你的函数在本质上有任何其他异步的东西。在它上打
async
并不能神奇地使它异步。它需要实际异步地做一些事情。所有的任务都在等待
任务。延迟()
正在做的是让其他东西可以更新,而不是被锁定在循环中。你应该找一个专门的线程或BackgroundWorker来做一些有点繁重的事情,比如这样。我建议你看看
BackgroundWorker
并考虑是否可以调整你的方法来使用它。BackgroundWorker实际上已过时。请使用Task.Run()和Dispatcher@GlorinOakenfoot谢谢你的解释。我实际上希望通过使它异步并移动到另一个类,它将在一个新线程上运行。不幸的是,我不知道如何利用backgroundworker或专用线程(很抱歉输入错误,我现在是手机用户,下班回家)没有人会对他正在“野蛮使用”guids感到有趣?好吧……这是一个标准的调用。例如:output=wait func();我现在可以移动,离家大约1小时,这是我能做出的最好的回答atm@DethoRhyne-是的,这是一个标准调用。它的目的是延迟某些活动。这是您想要做的吗?如果不是,您正在滥用此函数。目的是调用一个将迭代2^32或更少数字的函数。我希望应用程序执行异步因此UI会更新进度信息。这似乎是一个好主意,但我不知道如何实现。你能在函数中编写一个带有for循环的简单示例吗。比如说,任务完成后你将如何完成任务?我更新了我的答案。但你可能需要根据具体情况修改它。通过使用yo
var results = await Task.Run(() => GetSteamIds(guids, progress, progress2));