FileHelpers在调用engine.ReadNext()方法和readign engine.LineNumber属性之间发生线程锁定问题

时间:2010-04-12 17:09:44

标签: c# multithreading locking filehelpers

我使用带有FileHelpers库的生产者/消费者模式,使用多个线程从一个文件(可能很大)中导入数据。每个线程都应该导入该文件的一大块,我想使用FileHelperAsyncEngine实例的LineNumber属性来读取该文件作为导入行的主键。 FileHelperAsyncEngine内部有一个IEnumerator IEnumerable.GetEnumerator(); 使用engine.ReadNext()方法迭代。这内部设置LineNumber属性(似乎不是线程安全的)。

消费者将让生产者与他们联系,向消费者提供DataTables,消费者将通过SqlBulkLoad类使用它们,这将使用IDataReader实现,该实现将遍历Consumer实例内部的DataTable集合。每个实例都有一个与之关联的SqlBulkCopy实例。

我有线程锁定问题。下面是我如何创建多个Producer线程。我开始后面的每个线程。将调用生产者实例上的生成方法,确定将处理哪个输入文件块。 似乎engine.LineNumber不是线程安全的,我没有在数据库中导入正确的LineNumber。似乎在engine.LineNumber的时候读取了一些名为engine.ReadNext()的其他线程并更改了engine.LineNumber属性。我不想锁定应该处理一大块输入文件的循环,因为我松散了并行性。如何重新组织代码来解决这个线程问题?

由于 Rad

            for (int i = 0; i < numberOfProducerThreads; i++)
            DataConsumer consumer = dataConsumers[i];

            //create a new producer
            DataProducer producer = new DataProducer();

            //consumer has already being created
            consumer.Subscribe(producer);

            FileHelperAsyncEngine orderDetailEngine = new FileHelperAsyncEngine(recordType);
            orderDetailEngine.Options.RecordCondition.Condition = RecordCondition.ExcludeIfBegins;
            orderDetailEngine.Options.RecordCondition.Selector = STR_ORDR;

            int skipLines = i * numberOfBufferTablesToProcess * DataBuffer.MaxBufferRowCount;

            Thread newThread = new Thread(() =>
            {
                producer.Produce(consumer, inputFilePath, lineNumberFieldName, dict, orderDetailEngine, skipLines, numberOfBufferTablesToProcess);
                consumer.SetEndOfData(producer);
            }); 
            producerThreads.Add(newThread); thread.Start();}

    public void Produce(DataConsumer consumer, string inputFilePath, string lineNumberFieldName, Dictionary<string, object> dict, FileHelperAsyncEngine engine, int skipLines, int numberOfBufferTablesToProcess)
    {
        lock (this)
        {
            engine.Options.IgnoreFirstLines = skipLines;
            engine.BeginReadFile(inputFilePath);
        }

        int rowCount = 1;

        DataTable buffer = consumer.BufferDataTable;
        while (engine.ReadNext() != null)
        {
            lock (this)
            {
                dict[lineNumberFieldName] = engine.LineNumber;
                buffer.Rows.Add(ObjectFieldsDataRowMapper.MapObjectFieldsToDataRow(engine.LastRecord, dict, buffer));
                if (rowCount % DataBuffer.MaxBufferRowCount == 0)
                {
                    consumer.AddBufferDataTable(buffer);
                    buffer = consumer.BufferDataTable;
                }
                if (rowCount % (numberOfBufferTablesToProcess * DataBuffer.MaxBufferRowCount) == 0)
                {
                    break;
                }
                rowCount++;
            }
        }
        if (buffer.Rows.Count > 0)
        {
            consumer.AddBufferDataTable(buffer);
        }
        engine.Close();
    }

3 个答案:

答案 0 :(得分:2)

词典&LT;&GT;不是线程安全的。上面代码中的字典是正确锁定还是只用在你的锁中(这个)?

顺便说一句,我会避免使用lock(this)范例并使用通用对象来锁定代码。您可能遇到与特定资源无关的其他锁定问题。我在我的博客上详细说明了这个问题(Smart Resource Locking in C# .Net for Thread Safe Code)。 HTH

答案 1 :(得分:1)

你是对的LineNumber不是线程安全的:(

我只是调查代码,发现我们从内部阅读器读取了这一行,后来更新了LineNumber,因此根本没有线程安全。

问题在于,如果我们在内部添加一些sincronization代码,我们可以使事情变得非常慢,也许我们需要创建内部代码的线程安全版本以避免这种开销。

无论如何,我认为从性能角度来看,代码的较慢部分是文件操作,因此您无法使用多个线程进行读取。 也许您只需要一个线程将文件读取到工作队列,并且有多个线程可以读取它并使用每个记录,在这种情况下,您将获得所需的线程安全性

干杯

答案 2 :(得分:0)

我想我纠正了这个问题。这是字典&lt;&gt;需要锁定

锁定(字典) {    dict [lineNumberFieldName] = engine.LineNumber;    buffer.Rows.Add(ObjectFieldsDataRowMapper.MapObjectFieldsToDataRow(engine.LastRecord,dict,buffer)); } 感谢OmegaMan提供了一个很好的线索。