gpt4 book ai didi

c# - Task.WaitAll 死锁

转载 作者:行者123 更新时间:2023-12-01 19:29:08 28 4
gpt4 key购买 nike

我有一个关于 Task.WaitAll 的问题。起初我尝试使用 async/await 来得到这样的东西:

private async Task ReadImagesAsync(string HTMLtag)
{
await Task.Run(() =>
{
ReadImages(HTMLtag);
});
}

这个函数的内容并不重要,它同步工作,完全独立于外界。

我这样使用它:

private void Execute()
{
string tags = ConfigurationManager.AppSettings["HTMLTags"];

var cursor = Mouse.OverrideCursor;
Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait;
List<Task> tasks = new List<Task>();
foreach (string tag in tags.Split(';'))
{
tasks.Add(ReadImagesAsync(tag));
//tasks.Add(Task.Run(() => ReadImages(tag)));
}

Task.WaitAll(tasks.ToArray());
Mouse.OverrideCursor = cursor;
}

不幸的是,如果我以这种方式使用它(使用 async/await),我会在 Task.WaitAll 上陷入僵局。我的函数完成其工作(因此它们可以正确执行),但 Task.WaitAll 只是永远留在这里,因为显然 ReadImagesAsync 不会返回给调用者。

注释行是实际正确工作的方法。如果我注释 tasks.Add(ReadImagesAsync(tag)); 行并使用 tasks.Add(Task.Run(() => ReadImages(tag))); - 一切正常。

我在这里缺少什么?

ReadImages 方法如下所示:

private void ReadImages (string HTMLtag)
{
string section = HTMLtag.Split(':')[0];
string tag = HTMLtag.Split(':')[1];

List<string> UsedAdresses = new List<string>();
var webClient = new WebClient();
string page = webClient.DownloadString(Link);

var siteParsed = Link.Split('/');

string site = $"{siteParsed[0]} + // + {siteParsed[1]} + {siteParsed[2]}";

int.TryParse(MinHeight, out int minHeight);
int.TryParse(MinWidth, out int minWidth);

int index = 0;

while (index < page.Length)
{
int startSection = page.IndexOf("<" + section, index);
if (startSection < 0)
break;

int endSection = page.IndexOf(">", startSection) + 1;
index = endSection;

string imgSection = page.Substring(startSection, endSection - startSection);

int imgLinkStart = imgSection.IndexOf(tag + "=\"") + tag.Length + 2;
if (imgLinkStart < 0 || imgLinkStart > imgSection.Length)
continue;

int imgLinkEnd = imgSection.IndexOf("\"", imgLinkStart);
if (imgLinkEnd < 0)
continue;

string imgAdress = imgSection.Substring(imgLinkStart, imgLinkEnd - imgLinkStart);

string format = null;
foreach (var imgFormat in ConfigurationManager.AppSettings["ImgFormats"].Split(';'))
{
if (imgAdress.IndexOf(imgFormat) > 0)
{
format = imgFormat;
break;
}
}

// not an image
if (format == null)
continue;

// some internal resource, but we can try to get it anyways
if (!imgAdress.StartsWith("http"))
imgAdress = site + imgAdress;

string imgName = imgAdress.Split('/').Last();

if (!UsedAdresses.Contains(imgAdress))
{
try
{
Bitmap pic = new Bitmap(webClient.OpenRead(imgAdress));
if (pic.Width > minHeight && pic.Height > minWidth)
webClient.DownloadFile(imgAdress, SaveAdress + "\\" + imgName);
}
catch { }
finally
{
UsedAdresses.Add(imgAdress);
}
}

}
}

最佳答案

您正在同步等待任务完成。如果没有一点 ConfigureAwait(false) 魔法,这对 WPF 不起作用。这是一个更好的解决方案:

private async Task Execute()
{
string tags = ConfigurationManager.AppSettings["HTMLTags"];

var cursor = Mouse.OverrideCursor;
Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait;
List<Task> tasks = new List<Task>();
foreach (string tag in tags.Split(';'))
{
tasks.Add(ReadImagesAsync(tag));
//tasks.Add(Task.Run(() => ReadImages(tag)));
}

await Task.WhenAll(tasks.ToArray());
Mouse.OverrideCursor = cursor;
}

如果这是 WPF,那么我确信当某种事件发生时您会调用它。您应该从事件处理程序调用此方法,例如:

private async void OnWindowOpened(object sender, EventArgs args)
{
await Execute();
}

查看问题的编辑版本,我可以看到实际上您可以通过使用 DownloadStringAsync 的异步版本使一切变得非常漂亮:

private async Task ReadImages (string HTMLtag)
{
string section = HTMLtag.Split(':')[0];
string tag = HTMLtag.Split(':')[1];

List<string> UsedAdresses = new List<string>();
var webClient = new WebClient();
string page = await webClient.DownloadStringAsync(Link);

//...
}

现在,tasks.Add(Task.Run(() => ReadImages(tag))); 是怎么回事?

这需要了解SynchronizationContext。创建任务时,您可以复制调度该任务的线程的状态,以便在完成等待后可以返回该任务。当您调用没有 Task.Run 的方法时,您会说“我想回到 UI 线程”。这是不可能的,因为 UI 线程已经在等待任务,因此它们都在等待自己。当您添加另一个任务时,您是在说:“UI 线程必须调度一个‘外部’任务,该任务将调度另一个‘内部’任务,我稍后会再讨论。”

关于c# - Task.WaitAll 死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51801460/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com