在C#中多线程锁定DataTable的正确方法是什么?

6

这是正确的方式来锁定和修改由多个线程共享的DataTable吗?如果不是,那么正确的方式是什么?

private void DoGeocodeLookup(object info)
{
    ParameterRow data = (ParameterRow)info;
    DataRow dr = data.Dr;
    int localIndex = data.Index;
    ManualResetEvent doneEvent = data.m_doneEvent; 

    Geocode_Google.GeoCode gc = new GeoCode();

    gc.Addr_In = m_dtAddress.Rows[localIndex]["AddressStd_tx"].ToString();

    gc.GeoCodeOne();

    if (gc.Status == "OK")
    {
        //write back to temporary datatable
        lock( m_TempTable.Rows.SyncRoot )
        {
            m_TempTable.Rows[localIndex]["GL_Address"] = gc.Thoroughfare_tx; 
        }
    }
    doneEvent.Set(); 
}

我的结构:

struct ParameterRow
{
    private DataRow m_row;
    private int m_index;

    public DataRow Dr
    {
        get { return m_row; }
        set { m_row = value; }
    }

    public int Index
    {
        get { return m_index; }
        set { m_index = value; }
    }

    public ManualResetEvent m_doneEvent;

    public ParameterRow( DataRow row, int index, ManualResetEvent doneEvent)
    {
        m_row = row;
        m_index = index;
        m_doneEvent = doneEvent; 
    }
}

我启动所有线程的代码片段:

//make temporary table
m_TempTable = new DataTable();
m_TempTable = m_dtAddress.Copy();

for (int index = 0; index < m_geocodes; index++)
{
    doneEvents[index] = new ManualResetEvent(false);
    ThreadPool.QueueUserWorkItem(DoGeocodeLookup, new ParameterRow( m_dtAddress.Rows[index], index, doneEvents[index]));  
}

WaitHandle.WaitAll(doneEvents);

1
看起来这样做可能可行,但为什么不将DataTable声明为volatile呢? - Botonomous
那是更好的方式吗? - Cuthbert
我认为是这样的。这意味着每个线程都会获得最新版本。 - Botonomous
2
为什么要使用 DataTable 创建结构?根据 C# 标准,结构应该只由原始类型创建。结构大小应小于 16 字节。 - Kishore Kumar
@Anon - 将 DataTable 标记为 volatile 不会有任何帮助。volatile 只会影响存储在 m_TempTable 中的引用(如果将 m_TempTable 更改为指向不同的 DataTable,则每个线程都会立即看到更改)。它不会影响任何对象的内部状态,因此对于保护对 m_TempTable 的任何访问或更新没有任何作用。 - shf301
显示剩余2条评论
2个回答

8
你的示例不需要锁定DataTable。在DoGeocodeLookup中,你只进行了DataTable的读取操作。你对表格执行的唯一访问是查找行,这被视为读取操作。DataTable类被标记为适用于多线程读取操作,因此是安全的。如果你在DoGeocodeLookup中添加新行,则需要锁定。
你所更改的仅是由localIndex指定的单个DataRow中的数据。由于每次调用DoGeocodeLookup都使用不同的行 - 表中的单个行将始终只被一个线程更新,因此在这里没有同步问题。因此也不需要锁定。

0

这个线程非常有信息量,可能会对你的问题有所帮助。一般的共识是使用Interlock.Increment(要更新的对象)。锁定很慢,而且你必须记住所有可能锁定正在更新的对象的地方。而volatile并不意味着CPU A会立即看到CPU B刚刚更改的内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接