Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/296.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# DataTable的线程安全_C#_Multithreading_Datatable_Ado.net - Fatal编程技术网

C# DataTable的线程安全

C# DataTable的线程安全,c#,multithreading,datatable,ado.net,C#,Multithreading,Datatable,Ado.net,我读过这个答案,有些事情我不明白。 尤其是我看不懂这篇文章。我需要用什么包装纸? 有人能举个例子吗 我也不明白作者所说的级联锁和全锁是什么意思。请再举个例子。数据表根本不是为同时使用而设计或设计的(特别是在涉及任何形式的变异的情况下)。在我看来,这里可取的“包装”是: 无需同时处理数据表(涉及变异时),或: 删除数据表,改为使用直接支持所需内容(例如并发集合)或更简单且可以轻松同步(独占或读写器)的数据结构 基本上:改变问题 从评论中: 代码如下所示: Parallel.ForEach(s

我读过这个答案,有些事情我不明白。 尤其是我看不懂这篇文章。我需要用什么包装纸? 有人能举个例子吗


我也不明白作者所说的级联锁和全锁是什么意思。请再举个例子。

数据表
根本不是为同时使用而设计或设计的(特别是在涉及任何形式的变异的情况下)。在我看来,这里可取的“包装”是:

  • 无需同时处理
    数据表
    (涉及变异时),或:
  • 删除
    数据表
    ,改为使用直接支持所需内容(例如并发集合)或更简单且可以轻松同步(独占或读写器)的数据结构
基本上:改变问题


从评论中:

代码如下所示:

Parallel.ForEach(strings, str=>
{
    DataRow row;
    lock(table){
        row= table.NewRow();
    }
    MyParser.Parse(str, out row);
    lock(table){
        table.Rows.Add(row)
    }
});
我只能希望
out row
在这里是一个输入错误,因为这实际上不会导致它填充通过
NewRow()
创建的行,但是:如果您必须使用这种方法,您不能使用
NewRow
,因为挂起的行是共享的。你最好的选择是:

Parallel.ForEach(strings, str=> {
    object[] values = MyParser.Parse(str);
    lock(table) {
        table.Rows.Add(values);
    }
});
上面的重要变化是
覆盖了整个新行进程。请注意,在这样使用
Parallel.ForEach
时,您将无法保证顺序,因此最终顺序不需要精确匹配是很重要的(如果数据包含时间分量,这不应该是一个问题)

然而!我仍然认为您的做法是错误的:要使并行性相关,它必须是非平凡数据。如果您有非琐碎的数据,您真的不想将其全部缓冲在内存中。我强烈建议执行以下操作,在单个线程上可以很好地工作:

using(var bcp = new SqlBulkCopy())
using(var reader = ObjectReader.Create(ParseFile(path)))
{
    bcp.DestinationTable = "MyLog";
    bcp.WriteToServer(reader);    
}
...
static IEnumerable<LogRow> ParseFile(string path)
{
    using(var reader = File.OpenText(path))
    {
        string line;
        while((line = reader.ReadLine()) != null)
        {
            yield return new LogRow {
                // TODO: populate the row from line here
            };
        }
    }
}
...
public sealed class LogRow {
    /* define your schema here */
}

导言

如果并发性或并行性是目标程序的一项要求,则可以这样做。让我们来看两个示例(基本上,我们将看到方法在所有示例中的使用都很常见):

1-并行IT操作数据表:

NET提供本机资源以在DataTable上并行迭代,如下代码所示:

DataTable dt = new DataTable();
dt.Columns.Add("ID");
dt.Columns.Add("NAME");

dt.Rows.Add(1, "One");
dt.Rows.Add(2, "Two");
dt.Rows.Add(3, "Three");
dt.PrimaryKey = new DataColumn[] { dt1.Columns["ID"] };

Parallel.ForEach(dt.AsEnumerable(), row =>
{
    int rowId = int.Parse(row["ID"]);
    string rowName = row["NAME"].ToString();
    //TO DO the routine that useful for each DataRow object...
});
2-向数据表添加多个项目:

我认为这是一种非常重要的方法,因为DataTable的核心不是线程安全的集合/矩阵;然后,您需要ConcurrentBag的支持来保证不会破坏代码上的异常


在“”中,考虑到编程需要在数据表上使用并发性,我编写了一个详细的示例,其中包含了将数据表对象中的项添加到ConcurrentBag派生类中。然后,在享受并行资源的ConcurrentBag上添加程序业务规则后,可以将ConcurrentBag集合转换为DataTable。

面对同样的问题,我决定实现嵌套的ConcurrentDictionary

它是泛型的,但可以更改为使用定义的类型。 包括转换为DataTable的示例方法

/// <summary>
/// A thread safe data table
/// </summary>
/// <typeparam name="TX">The X axis type</typeparam>
/// <typeparam name="TY">The Y axis type</typeparam>
/// <typeparam name="TZ">The value type</typeparam>
public class HeatMap<TX,TY,TZ>
{
    public ConcurrentDictionary<TX, ConcurrentDictionary<TY, TZ>> Table { get; set; } = new ConcurrentDictionary<TX, ConcurrentDictionary<TY, TZ>>();

    public void SetValue(TX x, TY y, TZ val)
    {
        var row = Table.GetOrAdd(x, u => new ConcurrentDictionary<TY, TZ>());

        row.AddOrUpdate(y, v => val,
            (ty, v) => val);
    }

    public TZ GetValue(TX x, TY y)
    {
        var row = Table.GetOrAdd(x, u => new ConcurrentDictionary<TY, TZ>());

        if (!row.TryGetValue(y, out TZ val))
            return default;

        return val;

    }

    public DataTable GetDataTable()
    {
        var dataTable = new DataTable();

        dataTable.Columns.Add("");

        var columnList = new List<string>();
        foreach (var row in Table)
        {
            foreach (var valueKey in row.Value.Keys)
            {
                var columnName = valueKey.ToString();
                if (!columnList.Contains(columnName))
                    columnList.Add(columnName);
            }
        }

        foreach (var s in columnList)
            dataTable.Columns.Add(s);

        foreach (var row in Table)
        {
            var dataRow = dataTable.NewRow();
            dataRow[0] = row.Key.ToString();
            foreach (var column in row.Value)
            {
                dataRow[column.Key.ToString()] = column.Value;
            }

            dataTable.Rows.Add(dataRow);
        }

        return dataTable;
    }
}
//
///线程安全数据表
/// 
///X轴类型
///Y轴类型
///值类型
公共类热图
{
公共ConcurrentDictionary表{get;set;}=new ConcurrentDictionary();
公共无效设置值(TX x、TY y、TZ val)
{
var row=Table.GetOrAdd(x,u=>newConcurrentDictionary());
row.AddOrUpdate(y,v=>val,
(ty,v)=>val);
}
公共价值(TX x,TY y)
{
var row=Table.GetOrAdd(x,u=>newConcurrentDictionary());
如果(!row.TryGetValue(y,out-TZ-val))
返回默认值;
返回val;
}
公共数据表GetDataTable()
{
var dataTable=新的dataTable();
dataTable.Columns.Add(“”);
var columnList=新列表();
foreach(表中的var行)
{
foreach(行中的var valueKey.Value.Keys)
{
var columnName=valueKey.ToString();
如果(!columnList.Contains(columnName))
columnList.Add(columnName);
}
}
foreach(列列表中的变量s)
dataTable.Columns.Add;
foreach(表中的var行)
{
var dataRow=dataTable.NewRow();
dataRow[0]=row.Key.ToString();
foreach(row.Value中的var列)
{
dataRow[column.Key.ToString()]=column.Value;
}
dataTable.Rows.Add(dataRow);
}
返回数据表;
}
}

坦率地说,如果线程安全是一个问题,那么更好的方法是“停止使用DataTable”。您想做什么,为什么觉得DataTable是一个合适的解决方案?(提示:很少是这样)@marcGravel我想DataTable对象是用SqlBulkCopy填充基础的简单而舒适的方法。我使用它将解析后的日志加载到base中。a:日志加载不是需要并发性的场景(日志应该在传递到加载之前准备好),b:所有
SqlBulkCopy
需要的东西闻起来像
IDataReader
——您可以在任何对象模型上这样做(提供了
ObjectReader
,例如,它甚至可以与迭代器块一起工作,也称为
产生返回值
).就我个人而言,我认为这仍然是不可修改的,但我会在我的EDIT回答中对您的特定场景进行评论。我有一个大的日志文件,并在解析时使用并行性。但我需要一些容器,在加载到数据库之前收集解析的数据。常见代码如下:
Parallel.ForEach(strings,str=>{DataRow row;lock(table){row=table.NewRow();}MyParser.Parse(str,out row);lock(table){table.Rows.Add(row)}});
您能给出一些正确的解决方案吗?或者提供一些阅读链接,以正确的方式解决这个问题吗
/// <summary>
/// A thread safe data table
/// </summary>
/// <typeparam name="TX">The X axis type</typeparam>
/// <typeparam name="TY">The Y axis type</typeparam>
/// <typeparam name="TZ">The value type</typeparam>
public class HeatMap<TX,TY,TZ>
{
    public ConcurrentDictionary<TX, ConcurrentDictionary<TY, TZ>> Table { get; set; } = new ConcurrentDictionary<TX, ConcurrentDictionary<TY, TZ>>();

    public void SetValue(TX x, TY y, TZ val)
    {
        var row = Table.GetOrAdd(x, u => new ConcurrentDictionary<TY, TZ>());

        row.AddOrUpdate(y, v => val,
            (ty, v) => val);
    }

    public TZ GetValue(TX x, TY y)
    {
        var row = Table.GetOrAdd(x, u => new ConcurrentDictionary<TY, TZ>());

        if (!row.TryGetValue(y, out TZ val))
            return default;

        return val;

    }

    public DataTable GetDataTable()
    {
        var dataTable = new DataTable();

        dataTable.Columns.Add("");

        var columnList = new List<string>();
        foreach (var row in Table)
        {
            foreach (var valueKey in row.Value.Keys)
            {
                var columnName = valueKey.ToString();
                if (!columnList.Contains(columnName))
                    columnList.Add(columnName);
            }
        }

        foreach (var s in columnList)
            dataTable.Columns.Add(s);

        foreach (var row in Table)
        {
            var dataRow = dataTable.NewRow();
            dataRow[0] = row.Key.ToString();
            foreach (var column in row.Value)
            {
                dataRow[column.Key.ToString()] = column.Value;
            }

            dataTable.Rows.Add(dataRow);
        }

        return dataTable;
    }
}