This exception is thrown if you call a method or property of the Ftp object multiple times at the same time from several different threads. The Ftp object represents a single FTP session (or connection) that doesn't support multiple concurrent operations.
It's perfectly OK to use multiple threads to perform multiple simultaneous operations, but you have to use several instances of the Ftp object to make this possible. Using one Ftp instance per thread is the easiest way to achieve this (another is using a pool of Ftp objects, but this is more complicated and probably not needed in this case).
It's not entirely clear what is going on from your code snippet. I tried extending it to form the simplest possible console application I could use to reproduce the problem you describe, and this is what I ended up with:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using Rebex.Net;
namespace FtpThreads
{
public class FtpThreadTest
{
// added to make it compile
private static class ConnectionSettings
{
public static string proxy_host { get { return null; } }
public static int proxy_port { get { return 1080; } }
public static bool proxy_anonymous { get { return true; } }
public static string proxy_username { get { return null; } }
public static string proxy_password { get { return null; } }
public static int proxy_type_index { get { return 0; } }
public static FtpProxyType getProxyType(int index) { return FtpProxyType.None; }
public static string ftp_host { get { return "ftp.rebex.net"; } }
public static int ftp_port { get { return 21; } }
public static bool ftp_anonymous { get { return false; } }
public static string ftp_username { get { return "anonymous"; } }
public static string ftp_password { get { return "guest"; } }
public static bool ftp_passivemode { get { return true; } }
public static int ftp_protocol_index { get { return 0; } }
public static FtpSecurity getConnectSecurityType(int index) { return FtpSecurity.Unsecure; }
}
// added to make it compile
public class Transfer
{
}
public void CreateNewThread(Transfer trans) // made public to make it possible to test this
{
Thread trd; // added
trd = new Thread(new ParameterizedThreadStart(this.LoadFtp));
trd.IsBackground = true;
trd.Priority = ThreadPriority.Normal;
trd.Start(trans);
}
private void LoadFtp(object type)
{
Ftp transfer;
if ((Ftp)type == null)
{
transfer = new Ftp();
transfer.StateChanged += new FtpStateChangedEventHandler(client_StateChanged);
transfer.TransferProgress += new FtpTransferProgressEventHandler(transfer_TransferProgress);
}
else
{
//transfer = (Transfer)type; // not sure what this was supposed to do
transfer = (Ftp)type; // this at least compiles
}
FtpState state = OpenConnection(transfer);
// do something else to else it works
if (state == FtpState.Ready)
{
Console.WriteLine("Connected successfully from thread {0}.", Thread.CurrentThread.ManagedThreadId);
// get list of files - this takes some time
transfer.GetList();
}
else
{
Console.WriteLine("Not connected successfully from thread {0}: {1}", Thread.CurrentThread.ManagedThreadId, state);
}
// let the garbage collector dispose the object later
}
// added to make it compile
void transfer_TransferProgress(object sender, FtpTransferProgressEventArgs e)
{
}
// added to make it compile
void client_StateChanged(object sender, FtpStateChangedEventArgs e)
{
}
private FtpState OpenConnection(Ftp handler)
{
//try // no matching catch or finally block was found
{
TlsParameters par;
lock (handler)
{
handler.Proxy.ProxyType = ConnectionSettings.getProxyType(ConnectionSettings.proxy_type_index);
handler.Proxy.Host = ConnectionSettings.proxy_host;
handler.Proxy.Port = ConnectionSettings.proxy_port;
if (ConnectionSettings.proxy_anonymous)
{
handler.Proxy.UserName = String.Empty;
handler.Proxy.Password = String.Empty;
}
else
{
handler.Proxy.UserName = ConnectionSettings.proxy_username;
handler.Proxy.Password = ConnectionSettings.proxy_password;
}
handler.Passive = ConnectionSettings.ftp_passivemode;
handler.UploadBufferLength = 65000;
handler.Timeout = 120000;
handler.AbortTimeout = 1200000;
handler.Options = FtpOptions.KeepAliveDuringTransfer;
handler.KeepAliveDuringTransferInterval = 90;
par = new TlsParameters();
//par.CertificateVerifier = new FingerprintVerifier();
par.CertificateVerifier = CertificateVerifier.AcceptAll; // just to make it work easily
}
try
{
handler.Connect(ConnectionSettings.ftp_host, ConnectionSettings.ftp_port, par, ConnectionSettings.getConnectSecurityType(ConnectionSettings.ftp_protocol_index));
}
catch (StackOverflowException ex) // not sure why this is here - runtime stack overflows are usually fatal so the app would exit before your catch it
{
//SetText(handler.ToString() + ": " + ex.Message + "\r\n", textBox1);
Console.WriteLine(ex); // just display it
}
finally { }
if (!ConnectionSettings.ftp_anonymous)
handler.Login(ConnectionSettings.ftp_username, ConnectionSettings.ftp_password);
}
return handler.State; // added
}
}
// added to test the class
class Program
{
static void Main(string[] args)
{
FtpThreadTest threadCreator = new FtpThreadTest();
for (int i = 0; i < 8; i++)
{
Console.WriteLine("Creating new thread.", i);
threadCreator.CreateNewThread(null);
}
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
}
When I run this, it works fine and "Another operation is pending" error doesn't occur. We are unable to reproduce the issue you describe. To make sure you are in fact using multiple instances of Ftp object, please add the following line right below the transfer = new Ftp()
:
// create a log file
transfer.LogWriter = new Rebex.FileLogWriter(@"c:\temp\ftplog.txt", Rebex.LogLevel.Debug); // modify the path if needed
This will create a communication log where different instances of Ftp object are clearly distinguishable.
We know people have been using multiple Ftp objects from multiple threads for years without any problems, so unless there is a bug in your code, it is something very hard to reproduce. Posting a code that triggers the error (or modifying the test code above) would really help.
For a sample code that uses multiple threads to upload a list of files, check out the FtpsMultiUploader project. It doesn't raise "Another operation is pending" either, or at least no one reported that yet.
<