3

I have a multithreading program with 2 threads.

One thread is detecting the USB insertion and removal.

The second thread is responsible for transferring files to the USB (on USB insertion event). Once all the files are successfully copied to the USB then, the File Copying Thread (second thread) should enter into the “Successfully Copied” state and remain there till the USB is removed. As soon as the USB is removed, the isUSBInsterted flag is set to FALSE and the File Copying Thread (second thread) enters into the IDLE state.

 public enum FileTransferStates { Idle = 0, FileCopyingState = 1, SuccessfullyCopiedState = 2 }

    public void ExecuteUSBFileTransfer()
    {
        switch (CurrentState)
        {
            case FileTransferStates.Idle:
                IdleState();
                return;


            case FileTransferStates.FileCopyingState:

                FileCopyingState();
                ExecuteUSBFileTransfer();
                break;

            case FileTransferStates.SuccessfullyCopiedState:

                SuccessfullyCopiedState();
                ExecuteUSBFileTransfer();
                break;

            default:
                return;
        }
    }

    private void SuccessfullyCopiedState()
    {
       //Current state is "FileTransferStates.SuccessfullyCopiedState"

        if (!USB.isUSBInsterted)
            CurrentState = FileTransferStates.Idle; //Resetting the State if the USB is removed        
    }

QUESTION: Currently, I am calling the parent method (ExecuteUSBFileTransfer() ) again and again, if the thread has entered into SuccessfullyCopiedState(). I think this is a wastage of CPU resources. Moreover, a USB may remain inserted for very long periods. So, I would like that the thread sleeps for this duration until the USB is not removed. How can I remain in SuccessfullyCopiedState() and not check for the USB removal without wasting resources?

PS: Basically, I want to send the File Copying Thread into dormant stage inside the SuccessfullyCopiedState() method till the USB is not removed.

skm
  • 5,015
  • 8
  • 43
  • 104
  • https://msdn.microsoft.com/en-us/library/5hbefs30(v=vs.110).aspx I guess – ta.speot.is Mar 13 '17 at 11:50
  • 1
    It's hard to answer that based on pseudo code. Do you know about the difference between blocking and nonblocking I/O? Do you know about select() as low-level way to wait for inputs and / or signals? Has your library got a method that waits until an event occurs? Has your library got callbacks? In which thread are the callbacks executed? Do you know about threads and mutexes and Wait() and Notify()? – yeoman Mar 13 '17 at 11:56
  • Btw. why does the first method call itself recursively? Is this an attempt to simulate an event loop through recursion? The calls are end recursive, but I don't think .net has got end recursion optimization, or has it? Usually, event loops are implemented via loops, hence the name ^^. – yeoman Mar 13 '17 at 11:59
  • @yeoman: I am not using any external library. Both the threads (USB and FileCopyingThread) are started by the Main function (püublic static void main()). I know basic concepts about threads and Mutexes. I need something like global Notification from the USB thread that the USB is removed and the FileCopyingThread should sleep till it receives this notification. – skm Mar 13 '17 at 12:04
  • @yeoman: the First method is calling itself recursively.....and that's the reason I am asking this question. If my thread can sleep till it receives the notification that the USB has been received then I don't need to enter into the `private void SuccessfullyCopiedState()` again and again. – skm Mar 13 '17 at 12:06
  • Use normal thread signalling constructs (such as ManualResetEvent or Monitor.Pulse\Wait) instead of constantly checking boolean flag. – Evk Mar 13 '17 at 12:23

1 Answers1

2

You are looking for:

AutoResetEvent (ManualResetEvent)

Simply put: these will allow you to "freeze" a thread until it receives a "go" from another thread.

There's a full example from Eren Ersönmez in another question's answer, which uses 2 Worker threads, but is still very easy to grasp.

Essentially you have this AutoResetEvent object which will block execution of the thread when you call .WaitOne(). This "block" will be reset once you call .Set() from any thread you like.

Example:

static readonly AutoResetEvent fileCopyEvent = new AutoResetEvent(false);

bool keepFileCopyThreadAlive = true;
void FileCopyThread()
{
    while (keepFileCopyThreadAlive)
    {
        fileCopyEvent.WaitOne(); 
        if (!keepFileCopyThreadAlive) return; // Exit thread if told to.
        Console.WriteLine("Copying files...");
        Thread.Sleep(1000); // Do your stuff here
        Console.WriteLine("Done copying files, waiting for USB Thread.");
    }
}

At this point all you have to do is to call: fileCopyEvent.Set(); from your USB Thread. This will release the "block" and execute anything below fileCopyEvent.WaitOne();. Since this is an AutoResetEvent object it will automatically return to its "blocked" state and you don't have to do anything else. The loop will force the code to go back up to .WaitOne() and be ready for you next call to .Set().

Exiting the thread is as easy as setting keepFileCopyThreadAlive = false; and then calling the usual .Set(). Since there's a check to exit the loop once keepFileCopyThreadAlive == false

Note: the above code is untested, no compiler available right now.

Community
  • 1
  • 1
r41n
  • 908
  • 7
  • 18