简体   繁体   English

优雅地关闭多线程应用程序?

[英]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 第一个(主线程)使用套接字捕获数据并更新DataTables

The second Inserts the DataTables into the database. 第二个将DataTables插入数据库。

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. 应用程序工作正常,但当它关闭时,主线程完成读取数据并在第二个线程中调用Abort方法,这可能是插入数据库,这会导致数据不一致。

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           
        }
     }
}

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. 这是一个基于SizeQueue<>的完整示例 - 请注意,在读者作者完全退出之前,该过程不会退出。 If you don't want to have to drain the queue (ie you want to exit sooner, and forget any pending work), then fine - add an extra (volatile) flag somewhere. 如果您不想排空队列(即您想要更快退出,并忘记任何待处理的工作),那么很好 - 在某处添加一个额外的(volatile)标志。

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");
    }
}

Assuming "call abort method" means aborting the thread using Thread.Abort. 假设“call abort方法”意味着使用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. 尽管如此,当您的应用程序崩溃时,您不应该在数据库中获得不一致的数据,这就是您拥有具有ACID属性的数据库事务的原因。

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. 首先,事务可以使某些操作更快,例如尝试在表中插入10行,在事务中再次尝试,事务版本会更快。 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? 或者在查询分析器中针对数据库运行报告时?

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. 要关闭,请为每个线程设置“please now now”标志,然后使用“join”等待每个线程完成。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM