Our Products:   CompleteFTP  edtFTPnet/Free  edtFTPnet/PRO  edtFTPj/Free  edtFTPj/PRO
0 votes
6.8k views
in .NET FTP by
Hello,

I'm attempting to write a threaded file uploader, in order to upload files simultaneously. This is part of a larger project that uploads files to a remote server for processing, downloads the results and process the response file.

I read in the FAQ that the FTPClient object is not thread safe...

4. Is edtFTPnet thread safe?

No, this would incur a significant performance penalty for users that do not require a thread safe library. Instead, synchronize your own code to ensure multiple threads do not access the same FTPClient object.


However, the way I've written my code, I'm not sharing an FTPClient object. The object is local to the Upload function.

Thusfar, I've created a few objects to accomplish my task of processing these files.

I have a ThreadFile object, that is nothing more than a simple data construct. It simply holds the file's name (and path) and whether it has been uploaded, downloaded and/or processed. Nothing fancy here.

I have a ThreadFileManager, which takes a ThreadFile in it's constructor and will ultimately do all of the work (upload the file, wait for the response, download it, and process it). The ThreadFileManager class has a UploadFile method that looks like this:

public void UploadFile(string folderPath, string fileName) 
{
   string uploadFile = Path.Combine(folderPath, fileName);

   Console.WriteLine("{0}: beginning to uploadfile", Thread.CurrentThread.Name);

   FTPClient ftp = new FTPClient();
   ftp.RemoteHost = FTPhost;
   Console.WriteLine("{0}: created ftp instance", Thread.CurrentThread.Name);
   ftp.Connect();
   ftp.User(FTPuser);
   ftp.Password(FTPpass);

   ftp.Put(uploadFile, fileName);
   ftp.Quit();

   
   Console.WriteLine("{0}: {1} has been uploaded.", Thread.CurrentThread.Name, file.fileName);
   file.hasBeenUploaded = true;
}


Notice that I'm using a seperate instance of the FTPClient object here.

Here is how I'm calling all of this.

ThreadFile[] files = new ThreadFile[3];
files[0] = new ThreadFile(folderPath, "export2.txt");
files[1] = new ThreadFile(folderPath, "export3.txt");
files[2] = new ThreadFile(folderPath, "export4.txt");

Thread[] workers = new Thread[files.Length];

//instantiate the threads
Console.WriteLine("Instantiating threads");
for(int i=0; i < files.Length; i++) 
{
   Console.WriteLine("Creating ThreadFileManager for {0}", files[i].fileName);
   ThreadFileManager tfm = new ThreadFileManager(ref files[i]);
   workers[i] = new Thread(new ThreadStart(tfm.ThreadRun));
   workers[i].Name = Path.GetFileNameWithoutExtension(files[i].fileName) + "Worker";
}

//start the threads
Console.WriteLine("Starting threads");
for(int i=0; i < files.Length; i++)
{
   Console.WriteLine("Starting thread for {0}", files[i].fileName);
   workers[i].Start();
   while (!workers[i].IsAlive);
   Console.WriteLine("{0} is alive", workers[i].Name);
}

//wait for the threads to finish
Console.WriteLine("Waiting for threads to finish");
for(int i=0; i < files.Length; i++)
{
   Console.WriteLine("Waiting for {0} thread.", files[i].fileName);
   workers[i].Join();
   Console.WriteLine("Done");
}

Console.WriteLine("");
Console.WriteLine("Thread Test has completed");


Now when I run this, I get the following output:
Instantiating threads
Creating ThreadFileManager for export2.txt
Creating ThreadFileManager for export3.txt
Creating ThreadFileManager for export4.txt

Starting threads
Starting thread for export2.txt
export2Worker is alive
Starting thread for export3.txt
export3Worker is alive
Starting thread for export4.txt
export4Worker is alive

Waiting for threads to finish
Waiting for export2.txt thread.
'ThreadedFTP.exe': Loaded 'c:\projects\threadedftp\bin\debug\edtftpnet-1.1.3.dll', No symbols loaded.
ThreadFileManager running in export4Worker
ThreadFileManager running in export2Worker
ThreadFileManager running in export3Worker
export4Worker: beginning to uploadfile
export2Worker: beginning to uploadfile
export3Worker: beginning to uploadfile
WARNING: 'edtftp.log.level' not found - logging switched off
export4Worker: created ftp instance
export3Worker: created ftp instance
export2Worker: created ftp instance
export4Worker: export4.txt has been uploaded.
The thread 'export4Worker' (0xcfc) has exited with code 0 (0x0).
...


Only one of the threads ever instantiates the FTPClient object. THe others seem to be blocking. (Note the "beginning to uploadfile" and "created ftp instance" messages from export4Worker, but only the "beginning to uploadfile" from the others.

According to the FAQ, I should be able to do this (seperate instances, and all). What's the problem? Any ideas?

Thanks
Jason Habbley
jason@jayseven.com

BTW, just to verify that it is the FTP component that is causing the problem, I commented out all of the FTP object code in the upload function and replaced it with a Thread.Sleep(2000) to simulate work being done. It worked just fine, and all the threads returned properly.

2 Answers

0 votes
by
Pardon me,

I just realized that the output that I pasted was from an earlier version of the code. It appears as though it's blocking on the .Connect() method of the FTPClient object...

Here's how I've changed the UploadFile method to illustrate this:


public void UploadFile(string folderPath, string fileName) 
{
   string uploadFile = Path.Combine(folderPath, fileName);

   Console.WriteLine("{0}: beginning to uploadfile", Thread.CurrentThread.Name);

   FTPClient ftp = new FTPClient();
   ftp.RemoteHost = FTPhost;
   Console.WriteLine("{0}: created ftp instance", Thread.CurrentThread.Name);
   ftp.Connect();
   Console.WriteLine("{0}: upload process connected", Thread.CurrentThread.Name);
   ftp.User(FTPuser);
   ftp.Password(FTPpass);
   Console.WriteLine("{0}: upload process authenticated", Thread.CurrentThread.Name);

   ftp.Put(uploadFile, fileName);
   ftp.Quit();

   
   Console.WriteLine("{0}: {1} has been uploaded.", Thread.CurrentThread.Name, file.fileName);
   file.hasBeenUploaded = true;
}


and here's the correct output for this run....

Instantiating threads
Creating ThreadFileManager for export2.txt
Creating ThreadFileManager for export3.txt
Creating ThreadFileManager for export4.txt

Starting threads

Starting thread for export2.txt
export2Worker is alive
Starting thread for export3.txt
export3Worker is alive
Starting thread for export4.txt
export4Worker is alive

Waiting for threads to finish
Waiting for export2.txt thread.
'ThreadedFTP.exe': Loaded 'c:\projects\threadedftp\bin\debug\edtftpnet-1.1.3.dll', No symbols loaded.
ThreadFileManager running in export2Worker
ThreadFileManager running in export3Worker
ThreadFileManager running in export4Worker
export2Worker: beginning to uploadfile
export3Worker: beginning to uploadfile
export4Worker: beginning to uploadfile
WARNING: 'edtftp.log.level' not found - logging switched off
export2Worker: created ftp instance
export4Worker: created ftp instance
export3Worker: created ftp instance
export3Worker: upload process connected
export3Worker: upload process authenticated
The thread 'export3Worker' (0x870) has exited with code 0 (0x0).
export3Worker: export3.txt has been uploaded.
The program '[996] ThreadedFTP.exe' has exited with code 0 (0x0).


Note that in this case, only export3Worker was successful (connected and authenticated), while the other 2 worker processes were not successful (and seem to be hanging on the Connect method)

Thank you for any help you can provide,
Jason Habbley
jason@jayseven.com
0 votes
by (161k points)
We have been a bit busy with the 1.1.4 release. We'll take a look as soon as we can.

Categories

...