0

Business case:

I have a ASMX web service that copies/deletes/downloads files from some remote directory. My WebMethod creates an instance of the business class that performs all these operations. In case of downloading, I check which file is the latest one and downloads it. Once downloaded, I want to delete it immediately. However, in multithreaded scenario, there is good possibility that the file I try to delete is already deleted by some other thread.

My solution:

To avoid such issues, I want to web service execute only one thread at a time. Any other calls to the WebMethod should be waiting until previous thread completes the download and delete operation.

For this I have declared one static variable in my business class. At the start of my business method (which is called from the WebMethod) in the business class, I call the lock on this static object. So other web service calls will not execute the code in the business method until the lock on that static variable (object) is not released.

 public class FileOperator
{
    private static object locker = new object();

    public void DownloadAndDeleteFile(string fileName)
    {
        lock(locker)
        {
            // All business logic goes here.
        }
    }
}

The code in the WebMethod looks like this.

FileOperator fileOperator = new FileOperator();
        fileOperator.DownloadAndDeleteFile("File1.txt");

Questions:

  1. Is my solution correct?
  2. If yes, how to allow only one thread to execute from within the web service? Please note that I want to do this only for Downloading.. Uploading should work in parallel threads..
  3. Any better solution?

I am using .NET 4.0.

Learner
  • 4,661
  • 9
  • 56
  • 102

3 Answers3

2

Following is my understanding of your requirement:

  • Multiple clients / threads are executing a Web Method, which is downloading a file from a remote server and after download delete the downloaded file from the Remote server .

  • Now various threads, may go for a same or different file, to download.

  • Issue with the current implementation is, since you are having generic Mutex (lock) at the function level, so even if a thread wants to download a file, which is not accessed by any other thread, it has to wait as another thread is working on a different file, which will hit the performance big time as you scale the system, since it will synchronize all the clients, irrespective of the requirement.

I am just thinking about the possible solution on the lines that lock could be there based on the file / resource to be accessed on the remote server. Think on the following lines:

  • On server method maintain a list of static objects, where each object gets assigned to a different file / resource, you can maintain a dictionary, which maps a created static object to a file. When a thread comes with a file name, it gets the related object, if it already exists otherwise a new one gets created. Now, it tries to acquire lock on that object and it will get an access if it is the first thread otherwise it has to wait, if another thread is working on the same object. Inside the function you may check first whether file even exist, before proceeding forward or exit the mutex quickly.

  • There will be a generic lock valid for all threads, for a very short duration to just maintain the sanctitiy of the dictionary object for all threads. so, that each thread can check the existence of file

// Rough Implementation (You need to sanitize)

public class FileOperator { private static object locker = new object();

private static List<object> fileObjects;

private static Dictionary fileObjectsDictionary;     

public void DownloadAndDeleteFile(string fileName) 
{ 
    lock(locker) 
    { 
        if(fileObjects == null)
           fileObjects = new List<object>();

        if(fileObjectsDictionary == null)
           fileObjectsDictionary = new Dictionary();

        // check whether list contains fileName
        // For First time Add file to a given index
        // Create an Object using same index and add to the dictionary
        // For any other time get the index and fetch the object from dictionary
    } 

   lock(Dictionary Object)
    {
      // All business logic
    }
} 

}

// This way all threads will not wait at the same point

Hope this helps,

Mrinal

Mrinal
  • 36
  • 2
1

If your web service is the single point of entry, you actually shouldn't have to worry about this at all. Why? Because even though you can call your webservice asynchronously, the web service will execute the requests in a synchronous maner by default: Is ASMX WebService or WCF or aspx pages are async by default?

Community
  • 1
  • 1
Gaute Løken
  • 7,522
  • 3
  • 20
  • 38
0

Why don't you try to delete atomiclly?

lock(obj)
    if(IO.File.Exists(someFilePath))
       try
       {
           IO.File.Delete(someFilePath));
        }catch(IOException){//do nothing}
Fragilerus
  • 1,829
  • 3
  • 15
  • 22
  • In real time it's going to be call to some remote FTP server and that would be pretty heavy operation. Also, lets say the above code gave me the result that file ABC exists and so I try to delete it, but some other thread might have deleted it before I issues the delete request. There are many points of failure in the whole requirement. E.g. One thread decides which is the latest file in the directory and issues a request to download it. But some other thread might have deleted it by that time!.. So I am trying to avoid interefernce of any second thread! – Learner Jul 23 '12 at 10:28
  • 1
    That is why there is a Monitor lock around the existance check and deletion. Is there a different point where the file might be deleted? – Fragilerus Jul 23 '12 at 12:02