鉴于这一段代码,我发现我的UI封锁了一段时间(即使视窗弹出一条消息说该应用程序不响应。
using (var zip = await downloader.DownloadAsZipArchive(downloadUrl))
{
var temp = FileUtils.GetTempDirectoryName();
zip.ExtractToDirectory(temp); // BLOCKING CALL
if (Directory.Exists(folderPath))
{
Directory.Delete(folderPath, true);
}
var firstChild = Path.Combine(temp, folderName);
Directory.Move(firstChild, folderPath);
Directory.Delete(temp);
}
一些检查后,我发现,行说:
zip.ExtractToDirectory(temp);
是罪魁祸首。
我还以为我把它改成将足以使其工作:
await Task.Run(() => zip.ExtractToDirectory(temp));
但是...是一个很好的解决这个问题呢?
我有System.Reactive
背景(我都在与反应式编程),我想知道是否有处理这个更优雅的方式。
这是一个有点讨厌处于RX这样做。结合了Task<IDisposable>
粗糙。这是我得到了什么:
Observable
.FromAsync(() => downloader.DownloadAsZipArchive(downloadUrl))
.SelectMany(z =>
Observable
.Using(() => z, zip => Observable.Start(() =>
{
var temp = FileUtils.GetTempDirectoryName();
zip.ExtractToDirectory(temp); // BLOCKING CALL
if (Directory.Exists(folderPath))
{
Directory.Delete(folderPath, true);
}
var firstChild = Path.Combine(temp, folderName);
Directory.Move(firstChild, folderPath);
Directory.Delete(temp);
})))
.Subscribe();
是的,你可以想像ExtractToDirectory
需要时间,遗憾的是这种方法,它是一个CPU密集型工作负载的无async
版本。
你能做什么(有争议的),它是卸载到线程池,你会招致一个线程池线程点球,虽然,这意味着你采取一个线程池线程,(使用了宝贵的资源)阻止它。然而,由于Task
等待,这将腾出的UI环境。
await Task.Run(() => zip.ExtractToDirectory(temp));
请注意,虽然这将解决这个问题,在这里最好的方法是使用一个TaskCompletionSource
这基本上是任务事件(缺乏更好的话),这将节省不必要占用一个线程
更新通过olitee大评论
略少有争议的......你可以扩展这种使用方法:
await Task.Factory.StartNew(() => zip.ExtractToDirectory(temp), TaskCreationOptions.LongRunning);
这将迫使新的专用线程操作的创建。尽管会有用于创建线程,而不是再造一个集中的一个额外的处罚 - 但是这不是一个问题像这样的长时间运行的操作。
我将最有可能重构ZIP解压和目录创建代码到它自己的方法。这将使它在之后轻松卸载到一个线程。它还将具有使来电者决定是否要在另一个线程或不运行它的好处。
public void ExtractZip(ZipFile zip)
{
var temp = FileUtils.GetTempDirectoryName();
zip.ExtractToDirectory(temp); // BLOCKING CALL
if (Directory.Exists(folderPath))
{
Directory.Delete(folderPath, true);
}
var firstChild = Path.Combine(temp, folderName);
Directory.Move(firstChild, folderPath);
Directory.Delete(temp);
}
然后有顶层方法下载文件并解压缩zip
// this method contains async IO code aswell as CPU bound code
// that has been offloaded to another thread
public async Task ProcessAsync()
{
using (var zip = await downloader.DownloadAsZipArchive(downloadUrl))
{
// I would use Task.Run until it proves to be a performance bottleneck
await Task.Run(() => ExtractZip(zip));
}
}