Archived Forum Post

Index of archived forum posts

Question:

C# async await Task.Delay

May 17 '16 at 09:55

We have a series of web service api endpoints that we use that will do some movements of files via FTP and other web requests. Since the underlying processes task advantage of many of the async Task processes when we call things like .Wait/Result we will end up blocking (which is by design). For the sleep state for our methods, we're using the Task.Delay (as shown in the sample below).

This is my first shot of using Chilkat in an async fashion, If there was a better way of doing this or if this is at least a sane approach?

Second part of the question, wrapping the Chilkat.Ftp2 in a using statement, I haven't seen samples that show this, but I would assume since it's implements IDisposible that using the using syntax would be the ideal approach. Any advantages/disadvantages of this in the scope of Chilkat?

public async Task DeleteRemoteFileAsync(string url, string path, string filename, string username, string password, CancellationToken ct)
{
  try
  {
    List<string> fileList = new List<string>();
    using (Chilkat.Ftp2 ftp = _GetFTPConnection(url, username, password))
    {
      if (!ftp.ChangeRemoteDir(path))
      {
        throw new Exception(ftp.LastErrorText);
      }
      Chilkat.Task task = ftp.DeleteRemoteFileAsync(filename);
      if (task == null)
      {
        throw new Exception(ftp.LastErrorText);
      }
      if (!task.Run())
      {
        throw new Exception(ftp.LastErrorText);
      }
      while (!task.Finished && !ct.IsCancellationRequested)
      {
        //Sleep 100 ms.
        await Task.Delay(100, ct);
      }
      ftp.Disconnect();
    }
  }
  catch (Exception ex)
  {
    throw ex;
  }
}

private Chilkat.Ftp2 _GetFTPConnection(string url, string username, string password)
{
  try
  {
    Chilkat.Ftp2 ftp = new Chilkat.Ftp2();
    if (!ftp.UnlockComponent(CHILKATFTPKEY))
    {
      throw new Exception(ftp.LastErrorText);
    }
    if (!ftp.IsUnlocked())
    {
      throw new Exception("Chilkat.Ftp2 is not unlocked");
    }

    ftp.Hostname = url;
    if (!ftp.ConnectOnly())
    {
      throw new Exception(ftp.LastErrorText);
    }
    ftp.Username = username;
    ftp.Password = password;
    if (!ftp.LoginAfterConnectOnly())
    {
      throw new Exception(ftp.LastErrorText);
    }
    return ftp;
  }
  catch (Exception ex)
  {
    ex.Data["Url"] = url;
    ex.Data["Username"] = username;
    throw ex;
  }
}

Answer

Hey Gary,

I wrote a wrapper around the Chilkat Ftp Class and provided Methods with Task Result. I did it something like this:

public Task<FtpDeleteFileInfo> DeleteFileAsync(string fileName) {
    ThrowIfDisposed();
    ThrowIfNotConnected();
    ThrowIfOperationIsAlreadyRunning();

    var ftpTask = mFtpClient.DeleteRemoteFileAsync(fileName);
    if (ftpTask == null) {
        return Task.FromResult(new FtpDeleteFileInfo(mFtpClient.LastErrorText));
    }
    if (!ftpTask.Run()) {
        return Task.FromResult(new FtpDeleteFileInfo(ftpTask.LastErrorText));
    }

    RefreshTaskCompletionSource();

    var task = Task.Run(async () => {
        var ckResult = await mTaskCompletionSource.Task;
        if (!ckResult.Finished) {
            return new FtpDeleteFileInfo(ckResult.LastErrorText);
        }
        if (!ckResult.IsCompleted()) {
            return new FtpDeleteFileInfo(ckResult.ErrorTextDependingOnState());
        }
        if (ckResult.GetResultBool()) {
            return new FtpDeleteFileInfo();
        }

        return new FtpDeleteFileInfo(ckResult.ResultErrorText);
    });
    task.ConfigureAwait(false);
    return task;

}

Edit: You need to add the following lines, too:

mFtpClient.OnTaskCompleted += FtpClientOnOnTaskCompleted; // Necessary to know when the task is finished
private void FtpClientOnOnTaskCompleted(object sender, Chilkat.TaskCompletedEventArgs args) {
    mTaskCompletionSource.SetResult(args.Task);
    mOperationRunning = false;
}

With this approach the caller can use the async/await features, while using the Chilkat Build-In Async functionality. To use it take a look at TaskCompletionSource<>. FtpDeleteFileInfo ect is a custom class of mine.

HTH Stefan

Ps: Maybe you can upvote this: http://www.chilkatforum.com/questions/9476/c-asyncawait-with-async-methods Would be great when Chilkat do that.


Answer

See Asynchronous Programming with async and await (C#) for information about programming with async/await in C#.

In a nutshell, the C# async/await construct should be used with Chilkat's WinRT assembly which provides methods that return an IAsyncOperation, which corresponds to Task<tresult>. (This is C#'s Task class, not Chilkat's Task class.) You would do this if writing a WinRT (Windows Store) application. For example: http://www.example-code.com/csharp_winrt/ftp_download.asp

The asynchronous functionality of Chilkat for all other programming languages / environments is to return a Chilkat.Task object. An application can then start the task, go and do something else, check to see if it's finished, potentially get a TaskCompleted callback (depending on the programming language), etc. This has no relation to the async/await pattern in WinRT.

The Chilkat.Task asynchronous functionality is cross-language/cross-platform, and isn't quite the same as the async/await pattern in C#. The async/await pattern is really syntactic sugar -- the compiler generates code to do this: alt text

The non-WinRT Chilkat Async methods return a Chilkat.Task, which is the method call and arguments packaged up in a Chilkat.Task object that can be run at a later time. When you call Task.Run, the task is scheduled to run on a thread in Chilkat's background thread pool.