【问题标题】:Ordering of thread using ThreadPool.QueueUserWorkItem使用 ThreadPool.QueueUserWorkItem 对线程进行排序
【发布时间】:2023-03-29 22:52:01
【问题描述】:

我是线程基础知识的新手。

我有一个要对 XML 文件执行的操作队列(节点添加、节点删除等)

1]有 'n' 个 xml 文件,对于每个文件,线程池中的一个线程被分配使用 ThreadPool.QueueUserWorkItem 来做那些文件操作。

我想使用线程同时实现并发和操作顺序(重要)。
例如:假设如果要对文件“A.xml”执行操作 [a1,a2,a3,a4,a5]
和操作 [b1,b2,b3,b4,b5,b6,b7] 将在文件“B.xml”上执行.....
我想分配线程以便我可以在
中执行这些操作 同样的顺序,也同时发生(因为文件不同)。

2]也可以为每个操作分配一个线程,实现并发并保持顺序。

在 STA 模型中我做了类似的事情..

while(queue.count>0){
  File f = queue.Dequeue(); //get File from queue       
  OperationList oprlst = getOperationsForFile(f); 
// will get list-> [a1,a2,a3,a4,a5]   
  for each Operation oprn in oprlst 
  {
    performOperation(f,oprn)
    //in MTA i want to wait till operation "a1" completes and then operation "a2" will
   //start.making threads wait till file is in use or operation a(i) is in use.
  }    
}

我想在保存操作订单的同时执行此操作。 线程(操作)可以等​​待一个文件...但是 不同的操作需要不同的执行时间。

我尝试了 AutoResetEvent 和 WaitHandle.WaitAll(..) 但它使 while 循环 停止直到所有 'a(i)' 操作完成..我希望 a(i) 和 b(j) 同时执行。 (但在 a(i) 和 b(j) 中排序)

目前使用 .net 2.0。

这非常相似,是Question 提出的这个问题的一部分

【问题讨论】:

  • 是否有任何 a(i) 操作必须等待 b(i) 操作?
  • no..因为 a(i) 和 b(i) 将有不同的文件来处理。 all a(i) 将在 A.xml 上运行 ..all b(i) 在 B.xml 上

标签: c# concurrency multithreading


【解决方案1】:

要么为每个文件创建一个新线程,要么只为每个文件使用 one ThreadPool.QueueUserWorkItem 调用 - 无论哪种方式,您都希望文件的操作按顺序执行,所以有对该部分使用多个线程没有意义。在您的情况下,唯一可用的并行性是跨不同的文件,而不是操作。

【讨论】:

  • 我猜它现在正在发生的方式。我只是让操作以随机顺序发生..该代码有助于保持顺序。
  • 只在单个线程中顺序运行单个文件的所有操作肯定会保持顺序。无需每次操作启动一个线程。
  • 假设,我有 'n' 个线程和 m(> n) 个文件要处理..我怎样才能将现有线程重用于剩余文件???
  • 要么使用线程池,它会自动执行,要么创建一个生产者/消费者队列 - 创建 n 个消费者线程,将文件添加到线程读取的公共队列中。
  • 我在下面尝试了“线程实现”。它类似于“生产者-消费者”队列?但一旦线程完成工作,我就无法重用它。队列中的文件数不断变化,但线程数不变。
【解决方案2】:

您应该避免在ThreadPool 线程中使用线程阻塞技术,例如Monitor 锁和WaitHandle 结构,因为这些线程被其他进程使用。您需要让您的线程基于单个文件。如果单个文件不需要那么长时间来处理(并且您没有太多文件),那么ThreadPool 将起作用。

线程池实现

您可以在以文件为中心的方法上使用EnqueueUserWorkItem...类似这样:

private void ProcessFile(Object data)
{ 
    File f = (File)data;

    foreach(Operation oprn in getOperationsForFile(f))
    {
        performOperation(f, oprn);
    }
}

然后在处理文件的代码中,执行以下操作:

while(queue.Count > 0)
{
    ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessFile), queue.Dequeue());
}

如果您需要阻塞调用线程直到它们全部完成,那么WaitHandle 就可以了(因为您阻塞了自己的线程,不是ThreadPool 线程)。但是,您必须创建一个小的有效负载类来将其传递给线程:

private class Payload
{
    public File File;
    public AutoResetEvent Handle;
}

private void ProcessFile(Object data)
{ 
    Payload p = (Payload)data;

    foreach(Operation oprn in getOperationsForFile(p.File))
    {
        performOperation(f, oprn);
    }

    p.Handle.Set();
}

...

WaitHandle[] handles = new WaitHandle[queue.Count];
int index = 0;

while(queue.Count > 0)
{        
    handles[index] = new AutoResetEvent();

    Payload p = new Payload();

    p.File = queue.Dequeue();
    p.Handle = handles[index];

    ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessFile), p);

    index++;
}

WaitHandle.WaitAll(handles);

线程实现

但是,如果您有大量文件(或者处理文件可能需要大量时间),那么创建自己的线程是一个更好的主意。这也使您可以省略WaitHandles。

private void ProcessFile(File f)
{     
    foreach(Operation oprn in getOperationsForFile(f))
    {
        performOperation(f, oprn);
    }

    p.Handle.Set();
}

private object queueLock = new object();

private void ThreadProc()
{
    bool okToContinue = true;

    while(okToContinue)
    {
        File f = null;

        lock(queueLock)
        {
            if(queue.Count > 0) 
            {
                f = queue.Dequeue();
            }
            else
            {
                f = null;
            }
        }

        if(f != null)
        {
            ProcessFile(f);
        }
        else
        {
            okToContinue = false;
        }
    }
}

...

Thread[] threads = new Thread[20]; // arbitrary number, choose the size that works

for(int i = 0; i < threads.Length; i++)
{
    threads[i] = new Thread(new ThreadStart(ThreadProc));

    thread[i].Start();
}

//if you need to wait for them to complete, then use the following loop:
for(int i = 0; i < threads.Length; i++)
{
    threads[i].Join();
}

前面的例子是一个非常简陋的线程池,但它应该说明需要做什么。

【讨论】:

  • 我认为使用线程池比普通线程提高性能?在这种情况下会有帮助吗?
  • 它消除了启动您自己的线程的成本,但取决于您必须处理多少个文件,固定成本可能不是问题。还有一些不使用线程池的原因,例如如果您有许多操作要单独排队,或者任何给定的操作运行时间特别长。
  • 1]是否可以监控每个线程的进度? 2] 线程获取锁的顺序是什么?假设如果线程 t(i) 需要更长的时间来完成,并且如果多个线程说 t(i+1),t(i+2)..t(i+n) 正在等待锁.. 是保留的顺序?
  • 关于线程数,如果采用这种方式,那么线程会继续运行,直到队列耗尽;我的印象是您的队列是固定的;如果不是这种情况,并且您偶尔会添加项目,那么您必须保持工作线程处于活动状态并使用另一个WaitHandle 来发出信号以开始处理。这是一种更复杂的方法。
  • @Amitd:查看adam-robinson.net/blog/post/ProcessQueue.aspx 了解我整理的内容。这是我所说的更完整的示例,它处理随着时间的推移添加到队列中的项目。
【解决方案3】:

线程用于可以异步完成的操作,并且您希望您的操作同步完成。不过,文件的处理似乎可以异步完成(多线程)。

【讨论】:

  • 是的,它似乎也比基于 STA 的想法更好。
猜你喜欢
  • 1970-01-01
  • 2015-01-30
  • 2012-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多