Threading for FTP in C#

A

Asad

Hi,

I am trying to write some threading code to my application. The reason
I've been tempted to do this is because, I am doing some FTP uploads,
and sometimes during the put method, the application just hangs ("Not
Responding") instead of the regular timeout and return. I am using a
library to perform FTP stuff and so there may be a bug in there. In
any case, what I am concerned with is my application executing with
graceful execution and termination. I have plenty of try and catch
clauses inside the methods, and I even know which line is causing the
problem (the put line), I even tried debugging it line by line, but
sometimes it just hangs up. This is happening with only this one
particular FTP server being run by a client.

So, I am thinking if I should create a thread that calls the
doFTPUpload method. If the method doesn't return within, say, 60
seconds, I forcefully terminate the call and show an error to the user
via GUI. The main idea is to keep the application from going into "Not
Responding" state by running the error prone code in a different
thread.

Now I have never done threading before but have read about it. So I am
hoping someone can push me in the right direction.

I have a class called Transmission that has doFTPUpload method. It
takes in three arguments, a client object (that contains information
about the FTP such as server name, user id, password, etc.),a logs
object which is used to enter information into log files, and a
textbox txtSummary which shows information to user as upload
progresses.

I make a call from my main method as follows:

try
{
t.doFTPUpload(client,logs,txtSummary);
}
catch (Exception eUpload)
{
logs.addToLog("Upload Failed!");
logs.addToLog(eUpload.ToString());
}

Now how can I modify this code to start a new worker thread and make
the call to doFTPUpload inside this thread. Also how can I put a
timeout on it so that if the thread doesn't finish up in say 60
seconds, I manually kill the thread and go on with my execution inside
the main method?

Thanks.

Asad
 
D

Dave

I had fun writing this. It should keep you busy for a while.
FYI, I didn't try to build it, so check for type-o's.

Minimum "using" statments required:

using System;
using System.Windows.Forms;

Implementation (without class declaration):

private delegate void ShowStatusInvoker(string Text);
private delegate void FtpUploadInvoker(object client, object logs, TextBox txtSummary);
private FtpUploadInvoker ftpUploadInvoker; // Instance variable of delegate

public void DoUpload()
{
try
{
// Create a pointer to the doFTPUpload method
ftpUploadInvoker = new FtpUploadInvoker(t.doFTPUpload);

// Invoke the pointer asyncronously on a thread-pool thread.
// If the FTP class documentation states that the doFTPUpload method
// uses thread-pooling, then I recommend spawning a custom thread (not pooled)
// instead of using this implementation to prevent deadlocks.
// See System.Thread.Thread class.
IAsyncResult result = ftpUploadInvoker.BeginInvoke(client, logs, txtSummary,
new AsyncCallback(UploadComplete), null);

// Wait a maximum of 60 seconds for the invocation to complete
if (!result.IsCompleted && !result.AsyncWaitHandle.WaitOne(60000, false))
// Operation has not completed within the maximum time alotted
{
logs.addToLog("Upload Failed!");
logs.addToLog("Operation has not completed within the maximum time alotted");
}
}
catch (FtpUploadException eUpload)
{
logs.addToLog("Upload Failed!");
logs.addToLog(eUpload.ToString());
}
}

/// <summary>
/// Callback for doFTPUpload on a thread-pool thread. Do not call any
/// Begin* methods from within the context of this method to avoid thread-pool deadlocks.
/// </summary>
public void UploadComplete(IAsyncResult result)
{
if (ftpUploadInvoker != null)
// This method can be used to obtain the return value
// but it's not necessary since FtpUploadInvoker returns void
ftpUploadInvoker.EndInvoke(result);

// It is important to note that GUI calls must occur on the main window thread
// which is the thread that the message loop is executing on.
// Check Form.InvokeRequired and use the Invoke method to properly make GUI calls
// from this thread.
ShowStatus("Upload Complete");
}

/// <summary>
/// Displays the specified <paramref name="Text" /> using a thread-safe invocation.
/// </summary>
public void ShowStatus(string Text)
{
lock (this)
{
if (this.InvokeRequired) // Form.InvokeRequired
// It is assumed that this method will be defined within a derived Form class
this.Invoke(new ShowStatusInvoker(ShowStatusCore), new object[] { Text });
else
ShowStatusCore(Text);
}
}

/// <summary>
/// Displays the specified <paramref name="Text" />. This method is not thread-safe.
/// </summary>
private void ShowStatusCore(string Text)
{
txtStatus.Text = Text;
}
 
A

Asad Khan

Hi,

Ok that looks pretty good, but could you please explain your overall
program flow? I have never used delegates before and so I'm a little
lost despite the excellent comments. Appreciated.

Asad
 
D

Dave

No problem. LOL, here's a book on it:

FtpUploadInvoker is a delegate that has the same signature as the doFTPUpload method.
ftpUploadInvoker is an instance variable that will be assigned a reference to an instance of the doFTPUpload method.
(Mind the case)

DoUpload() is the "entry point" for the upload procedure:

First, it assigns the ftpUploadInvoker to an instance of the t class doFTPUpload method.
ftpUploadInvoker = new FtpUploadInvoker(t.doFTPUpload);

ftpUploadInvoker.BeginInvoke invokes the doFTPUpload method, asyncronously. This will call the doFTPUpload method and immediately
return to the calling code and continue execution.
The AsyncCallback is another delegate defined in System which allows the BeginInvoke method to callback to an application-defined
method when it is completed. Here, I'm telling it to callback to an instance of the UploadComplete method I defined later on:
IAsyncResult result = ftpUploadInvoker.BeginInvoke(client, logs, txtSummary,
new AsyncCallback(UploadComplete), null);

Check out the intellisense on the BeginInvoke method... it dynamically adjusts for your FtpUploadInvoker declaration.

Note: <null> is arbitrary data of Type, "Object" that I'm not using in this code. The IAsyncResult "result" variable has a
property that will let you access the data if you need to use it.

Next, the application pauses (although it doesn't have to) in order to wait for the asyncronous operation to complete. As I stated
above, when it's completed the "UploadComplete" method will automattically be invoked on a thread-pool thread. I set the timeout to
60000 milliseconds since you asked for a 60 second timeout:
if (!result.IsCompleted && !result.AsyncWaitHandle.WaitOne(60000, false))

WaitHandle.WaitOne returns false if the method call times-out before completing, so I used your log calls to log the error.

Note: <false> specifies that we do not want to exit any system-acquired locks before proceeding. Specifying true is useful for when
you are in a locked context, yet the asyncronous call will try to aquire the same lock (causing a dead-lock). You can acquire a
lock using the C# lock statement which I illustrated for you below. Locking synchronizes multiple threads by locking access to an
object. Only one thread may acquire a lock on an object at any given time. If the lock cannot be acquired, the thread will block
until the lock is acquired.

UploadComplete() is our asyncronous callback for when the doFTPUpload method completes:

EndInvoke allows us to retrieve any return value that the method may return, although this implementation declares the delegate as
void since I assumed that t.doFTPUpload returns void. (You weren't using any return value in your sample code). Because it's
returning void, I place the call to EndInvoke as an example, but without actually using an return value. Let's say, for example,
that doFTPUpload returns an Integer. You can modify this line to consume the return value as follows:
int doFTPUploadReturnValue = ftpUploadInvoker.EndInvoke(result);

ShowStatus() was intended to show you that UploadComplete is being called in a seperate thread than your application's main thread.
If you want to display something to the user upon an error (as you stated in your first post) or completion of the doFTPUpload
invocation while in the context of a different thread, you'll need to check if any of the Form controls require invocation. This is
because the form controls use the message loop to do work, and so methods on the controls may require an invocation on the
application's main thread to function properly. The invocation can be performed easily using the Form.Invoke() method which always
invokes the specified delegate on the thread that the message loop is running on. This is the call to the ShowStatus function,
which ensures that the control's are manipulated on the appropriate thread:
ShowStatus("Upload Complete");

ShowStatus():

Here's that C# "lock" statement I mentioned above. Locking "this" object, which should be a Form instance, will ensure that any
threads calling this method must wait for a previous invocation to first complete. You can lock any object that is not a ValueType,
but for simplicity I've used "this".
InvokeRequired returns true if the calling thread is not the thread that the message loop is running on, and if so I use
[Form].Invoke with a custom delegate to call the core implementation of the ShowStatus method. If InvokeRequired returns false, I
just call ShowStatusCore immediately since the call is on the thread that the message loop is running on:
lock (this)
{
if (this.InvokeRequired) // Form.InvokeRequired
// It is assumed that this method will be defined within a derived Form class
this.Invoke(new ShowStatusInvoker(ShowStatusCore), new object[] { Text });
else
ShowStatusCore(Text);
}

ShowStatusCore():

Place any code that you want here. I still recommend not using any Begin* methods in this scope because this method is invoked
indirectly from a method called on a thread-pool thread (UploadComplete).

Begin* methods:

It's stated that any system-defined functions that are labeled Begin* and are intended for asynchronous calls, of otherwise
synchronous methods, will be invoked using a thread-pool thread. The System.Threading.ThreadPool defines the maximum number of
running worker threads in the pool to be 25 * [Number of Processors]. This usually means 25. (There is also IOCompletionPort
threads, but I'm not going to discuss that here.) So if you have too many nested Begin* calls there is a chance of a dead-lock.
This means that one thread will be dependant on another thread being invoked, yet the latter thread will block until the calling
thread is freed. Dead-lock.

So, overall:

1. Asynchronous invocation of your upload method
2. Waits for a specified timeout before failing, or completes immediately
3. Runs a callback method if it completes before the timeout period elapses
4. Uses synchronization to alert the user of a change in application status that may occur on concurrent threads


Hope it helps :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

Serialport Threading 5
Threading Question 8
threading 6
Forms / Threading / Timers 1
C# threading in ASP.Net 4
Threading 1
asp.net1.1 threading problem 3
FTP loading in Windows Service 5

Top