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

发布于 2024-07-25 11:07:21 字数 813 浏览 5 评论 0原文

我有一个有两个线程的应用程序。

第一个线程(主线程)使用套接字捕获数据并更新 DataTables。

第二个线程将 DataTables 插入数据库。

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

目前我正在使用以下解决方案来克服“插入期间中止”

编辑: 在强有力的答案之后我改变了代码

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

淡淡離愁欲言轉身 2024-08-01 11:07:21

只要两个线程都没有标记为后台线程,应用程序就会继续运行,直到两个线程退出。 所以实际上,您需要做的就是分别让每个线程干净地退出。 对于写入数据库的线程来说,这可能意味着耗尽生产者/消费者队列并检查标志以退出。

我在此处展示了一个合适的生产者/消费者队列 - 工作人员就是:

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

这是一个基于 SizeQueue< 的完整示例;> - 请注意,在读取器写入器完全退出之前,该进程不会退出。 如果您不想排空队列(即您想早点退出,并忘记任何待处理的工作),那么很好 - 在某处添加一个额外的(易失性)标志。

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

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");
    }
}
有木有妳兜一样 2024-08-01 11:07:21

假设“调用 abort 方法”意味着使用 Thread.Abort 中止线程。 不要这样做

您实际上正在使您的应用程序崩溃。 有很多更简洁的方法可以使用监视器来做到这一点。

尽管如此,当您的应用程序崩溃时,您不应该在数据库中获得不一致的数据,这就是为什么您的数据库事务具有 ACID 属性。

非常重要的编辑
您说:出于性能原因您不使用事务,而是使用互斥体。 这在很多层面上都是错误。 首先,事务可以使某些操作更快,例如尝试向表中插入10行,在事务内再次尝试,事务版本会更快。 其次,当/如果您的应用程序崩溃时会发生什么,您是否会损坏您的数据库? 当您的应用程序的多个实例运行时会发生什么? 或者当您在查询分析器中针对数据库运行报告时?

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?

沦落红尘 2024-08-01 11:07:21

您的互斥等待应该涉及超时。 每个线程的外循环可以检查“请立即关闭”标志。 要关闭,请为每个线程设置“请立即关闭”标志,然后使用“join”等待每个线程完成。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文