Table of Contents

Threading and concurrency

SshSharp is asynchronous throughout, with no synchronous network calls and no hidden threads blocked on I/O. This page says what may be used from several tasks at once, and what may not.

What is safe to share

Object Concurrent use
SshClient Yes. Commands, sessions, SFTP clients, tunnels and forwarders can be started from many tasks at once; each is its own channel. This is what the shared connection of ISshConnectionProvider relies on.
SftpClient Yes. Requests from several tasks are pipelined on the one SFTP session, and replies are matched to their requests.
SshSession One reader per stream, one writer for StandardInput. Reading StandardOutput and StandardError from two tasks at the same time is expected (and needed, see flow control).
SshExpect One caller at a time. It reads StandardOutput itself; nothing else may read it.
Tunnel streams As for any Stream: one reader and one writer at a time, which may run concurrently.
SftpFileSystem, SftpFileProvider Yes, through their shared SftpClient.
SshClientOptions, SshConfigConnectOptions, SshConfig Immutable once built; one instance can serve many connections.
ISshClientFactory, ISshConnectionProvider Yes; singletons in the container.

Running commands in parallel on one connection:

// One connection, three channels at once: each command gets its own session.
var commands = new[] { "uptime", "free -m", "df -h /" };
var results = await Task.WhenAll(commands.Select(c => client.RunCommandAsync(c, cancellationToken)));
foreach (var (command, result) in commands.Zip(results))
{
    Console.WriteLine($"$ {command}\n{result.StandardOutputText}");
}

Callbacks

Callbacks you supply (host key verifiers, AskHostKey, KeyboardInteractive, PassphraseProvider, AuthenticationBanner, SshKeySigner.SignAsync, allowDestination filters) run on thread-pool threads, during connecting or while forwarding:

  • They may run concurrently when several connections are made at once, so share state in them safely.
  • They are asynchronous (ValueTask): await I/O in them rather than blocking.
  • A callback that prompts a person delays the connection until it answers; bound the prompt with the token it receives.

Synchronization contexts

The library does not capture a SynchronizationContext (it uses ConfigureAwait(false) internally), so awaiting it from a UI thread or a legacy ASP.NET request context does not deadlock as long as you await. Blocking on a task (.Result, .Wait()) is still a bad idea: it holds a thread for a network round trip.

Synchronous APIs

Two adapters are synchronous by contract: IFileProvider and many IFileSystem members. Each such call blocks a thread for an SFTP round trip, bounded by the adapter's OperationTimeout. Under load, prefer IFileSystem's ...Async methods and keep SftpFileProvider's cache on.

See also