Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gracefully closing multithreading application?

I have an application that has two threads.

The first one (the main thread) that captures the data using socket and update DataTables

The second Inserts the DataTables into the database.

The application works fine but when it closes, the main thread finishes reading the data and call Abort method in the second thread, which may be inserting into database and this leads to inconsistent data.

Currently I am using the following solution to overcome "aborting during insertion"

EDIT: After the powerful answers I changed the code

void MainThread()
{
     while(Read())
     {
        //Read Data through socket
        try
        {
           //Wait on Mutex1
           //Update Tables
        }
        finally
        {
          //Release Mutex1
        }
     }
   _isrunning = false;
   _secondThread.Join();
}
void SecondThread()
{
     while(_isrunning)
     {
        try
        {
           //Wait on Mutex1
           //Insert Tables into Database using transactions
        }
        finally
        {
           //Release Mutex1           
        }
     }
}
like image 799
Ahmed Avatar asked Jun 28 '09 09:06

Ahmed


3 Answers

As long as both threads are not marked as background threads, the app will keep running until both threads exit. So really, all you need to do is to get each thread separately to exit cleanly. In the case of the thread that writes to the database, this may mean exhausting a producer/consumer queue and checking a flag to exit.

I showed a suitable producer/consumer queue here - the worker would just be:

void WriterLoop() {
    SomeWorkItem item; // could be a `DataTable` or similar
    while(queue.TryDequeue(out item)) {
        // process item
    }
    // queue is empty and has been closed; all done, so exit...
}

Here's a full example based on SizeQueue<> - note that the process doesn't exit until the reader and writer have exited cleanly. If you don't want to have to drain the queue (i.e. you want to exit sooner, and forget any pending work), then fine - add an extra (volatile) flag somewhere.

static class Program {
    static void Write(object message) {
        Console.WriteLine(Thread.CurrentThread.Name + ": " + message);
    }
    static void Main() {
        Thread.CurrentThread.Name = "Reader";
        Thread writer = new Thread(WriterLoop);
        writer.Name = "Writer";
        var queue = new SizeQueue<int>(100);
        writer.Start(queue);
        // reader loop - note this can run parallel
        // to the writer
        for (int i = 0; i < 100; i++) {
            if (i % 10 == 9) Write(i);
            queue.Enqueue(i);
            Thread.Sleep(5); // pretend it takes time
        }
        queue.Close();
        Write("exiting");
    }
    static void WriterLoop(object state) {
        var queue = (SizeQueue<int>)state;
        int i;
        while (queue.TryDequeue(out i)) {
            if(i%10==9) Write(i);
            Thread.Sleep(10); // pretend it takes time
        }
        Write("exiting");
    }
}
like image 116
Marc Gravell Avatar answered Oct 11 '22 21:10

Marc Gravell


Assuming "call abort method" means aborting the thread using Thread.Abort. Don't do that.

You are effectively crashing your app. There are plenty cleaner ways of doing this with Monitors.

Nonetheless, you should not be getting inconsistent data in your DB when your app crashes that's why you have DB transactions which have the ACID properties.

VERY IMPORTANT EDIT You said: you do not use transactions for performance reasons, and instead use mutexes. This is WRONG on quite a few levels. Firstly, transactions can make certain operations faster, for example try inserting 10 rows into a table, try it again within a transaction, the transaction version will be faster. Secondly, what happens when/if your app crashes, do you corrupt your DB? What happens when multiple instances of your app are running? Or while you run reports against your DB in query analyzer?

like image 27
Sam Saffron Avatar answered Oct 11 '22 23:10

Sam Saffron


Your mutex wait should involve a timeout. Each thread's outer loop can check for a 'please close now' flag. To shut down, set the 'please close now' flag for each thread, then use 'join' to wait for each thread to finish.

like image 43
Andrew Avatar answered Oct 11 '22 22:10

Andrew