C# 如何使用ConcurrentQueue处理线程<;T>;

C# 如何使用ConcurrentQueue处理线程<;T>;,c#,multithreading,queue,concurrent-collections,C#,Multithreading,Queue,Concurrent Collections,我正在尝试找出处理队列的最佳方式。我有一个返回数据表的进程。每个数据表依次与前一个数据表合并。有一个问题,太多的记录要保存到最终的大容量拷贝(OutOfMemory) 因此,我决定应该立即处理每个传入的数据表。考虑一下ConcurrentQueue…但我不知道writequeueeddata()方法如何将表出列并写入数据库 例如: public class TableTransporter { private ConcurrentQueue<DataTable> tableQ

我正在尝试找出处理队列的最佳方式。我有一个返回数据表的进程。每个数据表依次与前一个数据表合并。有一个问题,太多的记录要保存到最终的大容量拷贝(OutOfMemory)

因此,我决定应该立即处理每个传入的数据表。考虑一下
ConcurrentQueue
…但我不知道
writequeueeddata()
方法如何将表出列并写入数据库

例如:

public class TableTransporter
{
    private ConcurrentQueue<DataTable> tableQueue = new ConcurrentQueue<DataTable>();

    public TableTransporter()
    {
        tableQueue.OnItemQueued += new EventHandler(WriteQueuedData);   // no events available
    }

    public void ExtractData()
    {
        DataTable table;

        // perform data extraction
        tableQueue.Enqueue(table);
    }

    private void WriteQueuedData(object sender, EventArgs e)
    {
        BulkCopy(e.Table);
    }
}

关于这个实现有什么问题吗?

根据我对这个问题的理解,您缺少了一些东西

并发队列是一种数据结构,旨在接受多个线程对队列的读写,而无需显式锁定数据结构。(所有jazz都是在幕后处理的,或者集合的实现方式不需要锁定。)

考虑到这一点,您尝试使用的模式似乎是“产品/消费者”。首先,您有一些任务生成工作(并将项目添加到队列)。第二个任务是消耗队列中的东西(并清除项目)


所以您确实需要两个线程:一个添加项目,另一个删除项目。因为您使用的是并发集合,所以可以有多个线程添加项目,也可以有多个线程删除项目。但显然,并发队列上的争用越多,成为瓶颈的速度就越快。

这是我提出的完整解决方案:

public class TableTransporter
{
    private static int _indexer;

    private CustomQueue tableQueue = new CustomQueue();
    private Func<DataTable, String> RunPostProcess;
    private string filename;

    public TableTransporter()
    {
        RunPostProcess = new Func<DataTable, String>(SerializeTable);
        tableQueue.TableQueued += new EventHandler<TableQueuedEventArgs>(tableQueue_TableQueued);
    }

    void tableQueue_TableQueued(object sender, TableQueuedEventArgs e)
    {
        //  do something with table
        //  I can't figure out is how to pass custom object in 3rd parameter
        RunPostProcess.BeginInvoke(e.Table,new AsyncCallback(PostComplete), filename);
    }

    public void ExtractData()
    {
        // perform data extraction
        tableQueue.Enqueue(MakeTable());
        Console.WriteLine("Table count [{0}]", tableQueue.Count);
    }

    private DataTable MakeTable()
    { return new DataTable(String.Format("Table{0}", _indexer++)); }

    private string SerializeTable(DataTable Table)
    {
        string file = Table.TableName + ".xml";

        DataSet dataSet = new DataSet(Table.TableName);

        dataSet.Tables.Add(Table);

        Console.WriteLine("[{0}]Writing {1}", Thread.CurrentThread.ManagedThreadId, file);
        string xmlstream = String.Empty;

        using (MemoryStream memstream = new MemoryStream())
        {
            XmlSerializer xmlSerializer = new XmlSerializer(typeof(DataSet));
            XmlTextWriter xmlWriter = new XmlTextWriter(memstream, Encoding.UTF8);

            xmlSerializer.Serialize(xmlWriter, dataSet);
            xmlstream = UTF8ByteArrayToString(((MemoryStream)xmlWriter.BaseStream).ToArray());

            using (var fileStream = new FileStream(file, FileMode.Create))
                fileStream.Write(StringToUTF8ByteArray(xmlstream), 0, xmlstream.Length + 2);
        }
        filename = file;

        return file;
    }

    private void PostComplete(IAsyncResult iasResult)
    {
        string file = (string)iasResult.AsyncState;
        Console.WriteLine("[{0}]Completed: {1}", Thread.CurrentThread.ManagedThreadId, file);

        RunPostProcess.EndInvoke(iasResult);
    }

    public static String UTF8ByteArrayToString(Byte[] ArrBytes)
    { return new UTF8Encoding().GetString(ArrBytes); }

    public static Byte[] StringToUTF8ByteArray(String XmlString)
    { return new UTF8Encoding().GetBytes(XmlString); }
}

public sealed class CustomQueue : ConcurrentQueue<DataTable>
{
    public event EventHandler<TableQueuedEventArgs> TableQueued;

    public CustomQueue()
    { }
    public CustomQueue(IEnumerable<DataTable> TableCollection)
        : base(TableCollection)
    { }

    new public void Enqueue (DataTable Table)
    {
        base.Enqueue(Table);
        OnTableQueued(new TableQueuedEventArgs(Table));
    }

    public void OnTableQueued(TableQueuedEventArgs table)
    {
        EventHandler<TableQueuedEventArgs> handler = TableQueued;

        if (handler != null)
        {
            handler(this, table);
        }
    }
}

public class TableQueuedEventArgs : EventArgs
{
    #region Fields
    #endregion

    #region Init
    public TableQueuedEventArgs(DataTable Table)
    {this.Table = Table;}
    #endregion

    #region Functions
    #endregion

    #region Properties
    public DataTable Table
    {get;set;}
    #endregion
}
公共类TableTransporter
{
专用静态int_索引器;
私有CustomQueue tableQueue=新CustomQueue();
私有函数RunPostProcess;
私有字符串文件名;
公共交通工具(
{
RunPostProcess=newfunc(序列化表);
tableQueue.TableQueued+=新的EventHandler(tableQueue\u TableQueued);
}
void tableQueue\u TableQueued(对象发送方,TableQueuedEventArgs e)
{
//用桌子做些什么
//我不知道如何在第三个参数中传递自定义对象
RunPostProcess.BeginInvoke(例如,表,新的异步回调(PostComplete),文件名);
}
公共数据()
{
//执行数据提取
entqueue(MakeTable());
WriteLine(“表计数[{0}]”,tableQueue.count);
}
私有数据表MakeTable()
{返回新的数据表(String.Format(“表{0}”,_indexer++);}
私有字符串序列化表(DataTable表)
{
字符串文件=Table.TableName+“.xml”;
数据集数据集=新数据集(Table.TableName);
dataSet.Tables.Add(表);
WriteLine(“[{0}]写入{1}”,Thread.CurrentThread.ManagedThreadId,文件);
string xmlstream=string.Empty;
使用(MemoryStream memstream=new MemoryStream())
{
XmlSerializer XmlSerializer=新的XmlSerializer(类型(数据集));
XmlTextWriter xmlWriter=新的XmlTextWriter(memstream,Encoding.UTF8);
serializer.Serialize(xmlWriter,dataSet);
xmlstream=UTF8ByteArrayToString(((MemoryStream)xmlWriter.BaseStream.ToArray());
使用(var fileStream=newfilestream(file,FileMode.Create))
Write(StringToUTF8ByteArray(xmlstream),0,xmlstream.Length+2);
}
文件名=文件;
返回文件;
}
私有作废完成后(IAsyncResult iasResult)
{
字符串文件=(字符串)iasResult.AsyncState;
WriteLine(“[{0}]已完成:{1}”,Thread.CurrentThread.ManagedThreadId,文件);
EndInvoke(iasResult);
}
公共静态字符串UTF8ByteArrayToString(字节[]ArrBytes)
{返回新的UTF8Encoding().GetString(ArrBytes);}
公共静态字节[]StringToUTF8ByteArray(字符串XmlString)
{返回新的UTF8Encoding().GetBytes(XmlString);}
}
公共密封类CustomQueue:ConcurrentQueue
{
公共事件EventHandler表已排队;
公共自定义队列()
{ }
公共自定义队列(IEnumerable TableCollection)
:base(TableCollection)
{ }
新的公共void排队(DataTable)
{
基本队列(表);
OnTableQueued(新的TableQueuedEventArgs(Table));
}
公共无效OnTableQueued(TableQueuedEventArgs表)
{
EventHandler=TableQueued;
if(处理程序!=null)
{
处理程序(此,表);
}
}
}
公共类TableQueuedEventArgs:EventArgs
{
#区域字段
#端区
#区域初始化
公共表QueueDeventargs(数据表)
{this.Table=Table;}
#端区
#区域功能
#端区
#区域属性
公共数据表
{get;set;}
#端区
}

作为概念的证明,它似乎工作得很好。我最多看到4个工作线程。

我认为
ConcurrentQueue
只在极少数情况下有用。它的主要优点是无锁。但是,通常生产者线程必须以某种方式通知消费者线程有数据可供处理。线程之间的这种信令需要锁,并且否定了使用
ConcurrentQueue
的好处。同步线程的最快方法是使用
Monitor.Pulse()
,它只在锁内工作。所有其他同步工具甚至更慢

当然,使用者只需不断地检查队列中是否有某些东西,这可以在没有锁的情况下工作,但这是对处理器资源的巨大浪费。更好的一点是,如果消费者在检查之间等待

在写入队列时引发线程是一个非常糟糕的主意。使用
ConcurrentQueue
来节省大约1微秒的时间将完全浪费在执行
eventhandler
上,这可能需要1000个tim
public class TableTransporter
{
    private static int _indexer;

    private CustomQueue tableQueue = new CustomQueue();
    private Func<DataTable, String> RunPostProcess;
    private string filename;

    public TableTransporter()
    {
        RunPostProcess = new Func<DataTable, String>(SerializeTable);
        tableQueue.TableQueued += new EventHandler<TableQueuedEventArgs>(tableQueue_TableQueued);
    }

    void tableQueue_TableQueued(object sender, TableQueuedEventArgs e)
    {
        //  do something with table
        //  I can't figure out is how to pass custom object in 3rd parameter
        RunPostProcess.BeginInvoke(e.Table,new AsyncCallback(PostComplete), filename);
    }

    public void ExtractData()
    {
        // perform data extraction
        tableQueue.Enqueue(MakeTable());
        Console.WriteLine("Table count [{0}]", tableQueue.Count);
    }

    private DataTable MakeTable()
    { return new DataTable(String.Format("Table{0}", _indexer++)); }

    private string SerializeTable(DataTable Table)
    {
        string file = Table.TableName + ".xml";

        DataSet dataSet = new DataSet(Table.TableName);

        dataSet.Tables.Add(Table);

        Console.WriteLine("[{0}]Writing {1}", Thread.CurrentThread.ManagedThreadId, file);
        string xmlstream = String.Empty;

        using (MemoryStream memstream = new MemoryStream())
        {
            XmlSerializer xmlSerializer = new XmlSerializer(typeof(DataSet));
            XmlTextWriter xmlWriter = new XmlTextWriter(memstream, Encoding.UTF8);

            xmlSerializer.Serialize(xmlWriter, dataSet);
            xmlstream = UTF8ByteArrayToString(((MemoryStream)xmlWriter.BaseStream).ToArray());

            using (var fileStream = new FileStream(file, FileMode.Create))
                fileStream.Write(StringToUTF8ByteArray(xmlstream), 0, xmlstream.Length + 2);
        }
        filename = file;

        return file;
    }

    private void PostComplete(IAsyncResult iasResult)
    {
        string file = (string)iasResult.AsyncState;
        Console.WriteLine("[{0}]Completed: {1}", Thread.CurrentThread.ManagedThreadId, file);

        RunPostProcess.EndInvoke(iasResult);
    }

    public static String UTF8ByteArrayToString(Byte[] ArrBytes)
    { return new UTF8Encoding().GetString(ArrBytes); }

    public static Byte[] StringToUTF8ByteArray(String XmlString)
    { return new UTF8Encoding().GetBytes(XmlString); }
}

public sealed class CustomQueue : ConcurrentQueue<DataTable>
{
    public event EventHandler<TableQueuedEventArgs> TableQueued;

    public CustomQueue()
    { }
    public CustomQueue(IEnumerable<DataTable> TableCollection)
        : base(TableCollection)
    { }

    new public void Enqueue (DataTable Table)
    {
        base.Enqueue(Table);
        OnTableQueued(new TableQueuedEventArgs(Table));
    }

    public void OnTableQueued(TableQueuedEventArgs table)
    {
        EventHandler<TableQueuedEventArgs> handler = TableQueued;

        if (handler != null)
        {
            handler(this, table);
        }
    }
}

public class TableQueuedEventArgs : EventArgs
{
    #region Fields
    #endregion

    #region Init
    public TableQueuedEventArgs(DataTable Table)
    {this.Table = Table;}
    #endregion

    #region Functions
    #endregion

    #region Properties
    public DataTable Table
    {get;set;}
    #endregion
}