C# 使用生产者/消费者模式和SQLServerDB中的SqlBulkCopy,使用多个线程处理分块的平面文件
我希望你能容忍我。我想提供尽可能多的信息。 主要问题是如何创建一个由多个线程使用的结构(如堆栈),这些线程将弹出一个值并使用它来处理一个大的平面文件,并且可能一次又一次地循环,直到处理完整个文件。 如果一个文件有100.000条记录,可由5个线程使用2.000行块处理 然后每个线程将得到10个块来处理 我的目标是在平面文件中移动数据(标题…子标题…详细信息,详细信息,详细信息,…详细信息,子标题,子标题…详细信息,详细信息,详细信息,…详细信息,子标题, Subheader…Detail,Detail,Detail,…Detail,suboter,Footer structure)转换为OLTP DB,恢复模式为Simple(可能是Full),分为3个表:第一个表代表Subheader行中存在的Subheader的唯一键,第二个表是中间表SubheaderGroup,代表2000条记录块中细节行的分组(需要将子标题的标识PK作为其FK,第3行表示FK指向子标题PK的明细行 我正在进行手动事务管理,因为我可以有成千上万的详细信息行 在加载过程中,我在目标表中使用一个特殊字段,该字段设置为0,然后在文件处理结束时,我正在执行一个事务更新,将该值更改为1,这可以向其他应用程序发出加载已完成的信号 我想将这个平面文件切分为多个相等的部分(相同数量的行),这些部分可以用多个线程处理,并使用从目标表元数据创建的IDataReader使用SqlBulkCopy导入 我想使用生产者/消费者模式(如下面链接-pdf分析和代码示例中所述)将SqlBulkCopy与SqlBulkCopyOptions.TableLock选项一起使用。 此模式允许创建多个生产者,并且需要订阅生产者才能使用该行的同等数量的消费者 在TestSqlBulkCopy项目的DataProducer.cs文件中,有一种方法可以模拟数千条记录的生成C# 使用生产者/消费者模式和SQLServerDB中的SqlBulkCopy,使用多个线程处理分块的平面文件,c#,sql-server,multithreading,flat-file,sqlbulkcopy,C#,Sql Server,Multithreading,Flat File,Sqlbulkcopy,我希望你能容忍我。我想提供尽可能多的信息。 主要问题是如何创建一个由多个线程使用的结构(如堆栈),这些线程将弹出一个值并使用它来处理一个大的平面文件,并且可能一次又一次地循环,直到处理完整个文件。 如果一个文件有100.000条记录,可由5个线程使用2.000行块处理 然后每个线程将得到10个块来处理 我的目标是在平面文件中移动数据(标题…子标题…详细信息,详细信息,详细信息,…详细信息,子标题,子标题…详细信息,详细信息,详细信息,…详细信息,子标题, Subheader…Detail,Det
public void Produce (DataConsumer consumer, int numberOfRows) {
int bufferSize = 100000;
int numberOfBuffers = numberOfRows / bufferSize;
for (int bufferNumber = 0; bufferNumber < numberOfBuffers; bufferNumber++) {
DataTable buffer = consumer.GetBufferDataTable ();
for (int rowNumber = 0; rowNumber < bufferSize; rowNumber++) {
object[] values = GetRandomRow (consumer);
buffer.Rows.Add (values);
}
consumer.AddBufferDataTable (buffer);
}
}
public void product(数据消费者,int numberOfRows){
int bufferSize=100000;
int numberOfBuffers=numberOfRows/bufferSize;
对于(int bufferNumber=0;bufferNumber
此方法将在新线程的上下文中执行。我希望此新线程仅读取原始平面文件的唯一区块,另一个线程将开始处理下一个区块。然后,使用者将使用SqlBulkCopy ADO.NET类将数据(泵送到他们的数据)移动到SQL Server DB
所以这里的问题是关于主程序,它规定了每个线程应该处理从lineFrom到lineTo的内容,我认为这应该在线程创建期间发生。
第二种解决方案可能是让线程共享某些结构,并使用它们独有的东西(如线程编号或序列号)查找共享结构(可能是堆栈并弹出值(执行此操作时锁定堆栈)然后下一个线程将拾取下一个值。主程序将拾取平面文件并确定块的大小并创建堆栈
那么,有没有人能提供一些代码片段,比如多个线程如何处理一个文件而只获取该文件的唯一部分
谢谢,
Rad对我来说,最有效的方法是使用队列保存未处理的工作,使用字典跟踪正在进行的工作:
using System;
using System.Collections.Generic;
using System.Threading;
namespace threadtest
{
public delegate void DoneCallbackDelegate(int idArg, bool successArg, string messageArg);
class Program
{
static void Main(string[] args)
{
Supervisor supv = new Supervisor();
supv.LoadQueue();
supv.Dispatch();
}
}
public class Supervisor
{
public Queue<Worker> pendingWork = new Queue<Worker>();
public Dictionary<int, Worker> activeWork = new Dictionary<int, Worker>();
private object pendingLock = new object();
private object activeLock = new object();
private int maxThreads = 200;
public void LoadQueue()
{
for (int i = 0; i < 1000; i++)
{
Worker worker = new Worker();
worker.Callback = new DoneCallbackDelegate(WorkerFinished);
lock (pendingLock)
{
pendingWork.Enqueue(worker);
}
}
}
public void Dispatch()
{
int activeThreadCount;
while (true)
{
lock (activeLock) { activeThreadCount = activeWork.Count; }
while (true)
{
lock (activeLock)
{
if (activeWork.Count == maxThreads) break;
}
lock (pendingWork)
{
if (pendingWork.Count > 0)
{
Worker worker = pendingWork.Dequeue();
Thread thread = new Thread(new ThreadStart(worker.DoWork));
thread.IsBackground = true;
worker.ThreadId = thread.ManagedThreadId;
lock (activeLock) { activeWork.Add(worker.ThreadId, worker); }
thread.Start();
}
else
{
break;
}
}
}
Thread.Sleep(200); // wait to see if any workers are done (many ways to do this)
lock (pendingLock)
lock (activeLock)
{
if ((pendingWork.Count == 0) && (activeWork.Count == 0)) break;
}
}
}
// remove finished threads from activeWork, resubmit if necessary, and update UI
public void WorkerFinished(int idArg, bool successArg, string messageArg)
{
lock (pendingLock)
lock (activeLock)
{
Worker worker = activeWork[idArg];
activeWork.Remove(idArg);
if (!successArg)
{
// check the message or something to see if you should resubmit thread
pendingWork.Enqueue(worker);
}
// update UI
int left = Console.CursorLeft;
int top = Console.CursorTop;
Console.WriteLine(string.Format("pending:{0} active:{1} ", pendingWork.Count, activeWork.Count));
Console.SetCursorPosition(left, top);
}
}
}
public class Worker
{
// this is where you put in your problem-unique stuff
public int ThreadId { get; set; }
DoneCallbackDelegate callback;
public DoneCallbackDelegate Callback { set { callback = value; } }
public void DoWork()
{
try
{
Thread.Sleep(new Random().Next(500, 5000)); // simulate some effort
callback(ThreadId, true, null);
}
catch (Exception ex)
{
callback(ThreadId, false, ex.ToString());
}
}
}
}
使用系统;
使用System.Collections.Generic;
使用系统线程;
命名空间线程测试
{
公共委托void DoneCallbackDelegate(int-idArg、bool-successArg、string-messageArg);
班级计划
{
静态void Main(字符串[]参数)
{
主管supv=新主管();
supv.LoadQueue();
supv.Dispatch();
}
}
公营班主任
{
public Queue pendingWork=新队列();
公共字典activeWork=新字典();
私有对象pendingLock=新对象();
私有对象activeLock=新对象();
私有int maxThreads=200;
公共void加载队列()
{
对于(int i=0;i<1000;i++)
{
工人=新工人();
worker.Callback=new DoneCallbackDelegate