c# - 如何使用 Task.Factory.FromAsync 在 Renci.SshNet.BeginDownload 中异步/等待

标签 c# asynchronous task iasyncoperation

我可以访问 Connected Renci.SshNet.SftpClient,我用它来获取 sftp 文件夹中的一系列文件。用于此的功能是

Renci.SshNet.SftpClient.ListDirectory(string);

由于目录中有大量文件,这大约需要 7 秒。我希望能够使用 async/await 和 cancellationToken 保持我的 UI 响应。

如果 Renci.SshNet 有一个返回任务的 ListDirectoryAsync 函数,那么这将很容易:

async Task<IEnumerable<SftpFiles> GetFiles(SftpClient connectedSftpClient, CancellationToken token)
{
    var listTask connectedSftpClient.ListDirectoryAsync();
    while (!token.IsCancellatinRequested && !listTask.IsCompleted)
    {
        listTask.Wait(TimeSpan.FromSeconds(0.2);
    }
    token.ThrowIfCancellationRequested();
    return await listTask;
}

唉,SftpClient 没有异步功能。以下代码有效,但在下载过程中不会取消:

public async Task<IEnumerable<SftpFile>> GetFilesAsync(string folderName, CancellationToken token)
{
    token.ThrowIfCancellationRequested();
    return await Task.Run(() => GetFiles(folderName), token);
}

但是,SftpClient 确实有一种使用函数的异步功能

public IAsyncResult BeginListDirectory(string path, AsyncCallback asyncCallback, object state, Action<int> listCallback = null);
Public IEnumerable<SftpFile> EndListDirectory(IAsyncResult asyncResult);

文章中Turn IAsyncResult code into the new async and await Pattern描述了如何将 IAsyncResult 转换为 await。

但是我不知道如何处理 BeginListdirectory 中的所有参数以及将 EndListDirectory 放在哪里。任何人都可以将其转换为一个任务,我可以等待很短的超时来检查取消 token ?

最佳答案

它看起来像 SftpClient不符合标准APM pattern : listCallback是 Begin 方法的一个额外参数。因此,我很确定您不能使用标准 FromAsync工厂方法。

但是,您可以使用 TaskCompletionSource<T> 编写自己的代码.有点尴尬,但可行:

public static Task<IEnumerable<SftpFile>> ListDirectoryAsync(this SftpClient @this, string path)
{
  var tcs = new TaskCompletionSource<IEnumerable<SftpFile>>();
  @this.BeginListDirectory(path, asyncResult =>
  {
    try
    {
      tcs.TrySetResult(@this.EndListDirectory(asyncResult));
    }
    catch (OperationCanceledException)
    {
      tcs.TrySetCanceled();
    }
    catch (Exception ex)
    {
      tcs.TrySetException(ex);
    }
  }, null);
  return tcs.Task;
}

(在浏览器中编写的代码,完全未经测试:)

我将其构造为扩展方法,这是我更喜欢的方法。这样您的消费代码就可以非常自然地执行 connectedSftpClient.ListDirectoryAsync(path)一种呼唤。

关于c# - 如何使用 Task.Factory.FromAsync 在 Renci.SshNet.BeginDownload 中异步/等待,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31188202/

相关文章:

c# - 在 C# 中显示 unicode 文本

javascript - 如何保证异步返回值在函数返回前被保存?

c# - await/async 与 "classic"异步(回调)

Android Activity 历史堆栈问题

c# - 如何在数据绑定(bind)时以编程方式向 datagridview 添加一行?

c# - 未知属性 xsi :type in XmlSerializer

linux - POSIX 一体机 : Any (good) way to correlate completion notifications back to original request?

groovy - 如何为新任务类型扩展 Gradle 任务的行为?

iOS - 使用 Titanium 每小时重复一项任务

c# - 对象初始值设定项、非公共(public)子项和嵌套初始化