5

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

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

3 回答 3

8

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

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

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");
    }
}
于 2009-06-28T09:42:27.760 回答
6

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

您实际上正在使您的应用程序崩溃。使用 Monitors 有很多更简洁的方法。

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

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

于 2009-06-28T09:44:38.363 回答
3

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

于 2009-06-28T11:14:25.000 回答