【问题标题】:How to pass parameters to threads that have already started running如何将参数传递给已经开始运行的线程
【发布时间】:2018-08-31 19:13:32
【问题描述】:

您好,我有一个应用程序使用一个线程将缓冲区从*src 复制到*dst,但我希望在程序开始时启动线程。当我想使用线程时,我想将*src*dstsize 传递给线程,以便它可以开始复制缓冲区。我该如何做到这一点?因为当我启动一个线程时,我会在实例化对象时传递值,ThreadX 在创建线程时传递。

Thread^ t0 = gcnew Thread(gcnew ThreadStart(gcnew ThreadX(output, input, size), &ThreadX::ThreadEntryPoint));

总结一下我想这样做:

  1. 程序开始
  2. 创建一个线程
  3. 在线程中等待
  4. 传递参数并唤醒线程开始复制
  5. 在线程中完成复制后,让主线程知道它已完成
  6. 就在程序结束之前加入线程

示例代码如下所示。

谢谢!

#include "stdafx.h"
#include <iostream>
#if 1

using namespace System;
using namespace System::Diagnostics;
using namespace System::Runtime::InteropServices;
using namespace System::Threading;

public ref class ThreadX
{
    unsigned short* destination;
    unsigned short* source;
    unsigned int num;

public:
    ThreadX(unsigned short* dstPtr, unsigned short* srcPtr, unsigned int size)
    {
        destination = dstPtr;
        source = srcPtr;
        num = size;
    }

    void ThreadEntryPoint()
    {
        memcpy(destination, source, sizeof(unsigned short)*num);
    }

};


int main()
{

    int size = 5056 * 2960 * 10; //iris 15 size
    unsigned short* input; //16bit
    unsigned short* output;
    Stopwatch^ sw = gcnew Stopwatch();

    input = new unsigned short[size];
    output = new unsigned short[size];

    //elapsed time for each test
    int sw0;
    int sw1;
    int sw2; 
    int sw3;

    //initialize input
    for (int i = 0; i < size; i++) { input[i] = i % 0xffff; }

    //initialize output
    for (int i = 0; i < size; i++) { output[i] = 0; }


    // TEST 1 //////////////////////////////////////////////////////////////////////
    for (int i = 0; i < size; i++) { output[i] = 0; }

    //-----------------------------------------------------------------------
    Thread^ t0 = gcnew Thread(gcnew ThreadStart(gcnew ThreadX(output, input, size), &ThreadX::ThreadEntryPoint));
    t0->Name = "t1";
    t0->Start();
    t0->Join();
    //-----------------------------------------------------------------------


    return 0
}

【问题讨论】:

    标签: c++ .net multithreading thread-safety c++-cli


    【解决方案1】:

    基本上,您需要一些基本的构建块来解决这个问题(我假设您只想执行一次此复制操作。如果您有恒定的输入流,我们可以轻松扩展解决方案):

    1) 共享内存 - 用于交换控制信息。在这种情况下,它将是源缓冲区指针、目标缓冲区指针和大小(从主线程到工作线程)。当工作完成时,您还需要一些数据结构(让我们从一个简单的布尔标志开始)以反向(从工作线程到主线程)共享信息。

    2) 条件变量 - 从主线程向工作线程发送信号,方向相反。因此,您需要 2 个不同的条件变量。

    3) 一个像互斥锁一样的同步原语来保护共享内存(因为它们会被两个线程同时访问)

    鉴于这些构建块,您的程序的伪代码将如下所示:

    struct Control {
         void* src, *dest;
         int num_of_bytes = -1;
         bool isDone = false;
         conditional_var inputReceived;
         conditional_var copyDone;
         mutex m;
    };
    
     void childThread() {
         m.lock();
         while (num_of_bytes == -1) {
             inputReceived.wait(m); // wait till you receive input.
         }
         // Input received. Make sure you set src and dest pointers, before setting num_of_bytes
         mempcy(dest, src, num_of_bytes);
         isDone = true; // mark work completion.
         copyDone.notify(); // notify the main thread of work completion.
         m.unlock();
    }
    
    
    void mainThread()
    {
          // Create worker thread at start;
          thread_t thread = pthread_create(&childThread);
    
          // Do other stuff...
          //
          //
    
          // Input parameters received. Set control information, and notify the 
          //  workerthread.
          mutex.lock();
          src = input.src;
          dest = input.dest;
          num_of_bytes = input.num_of_bytes;
    
          inputReceived.notify(); // wake up worker thread.
    
          while (!isDone) { // Wait for copy to be over.
               copyDone.wait();
          }
    
          m.unlock();  // unlock the mutex.
          thread.join(); // wait for thread to join. If the thread has already ended before we execute thread join, it will return immediately.
    
    }
    

    如果你想扩展这个解决方案来处理输入流,我们可以使用 2 个队列来处理请求和响应,队列的每个元素都是输入和输出参数。

    【讨论】:

    • 谢谢,但是如果我想为 childThread() 设置多个线程,那么它不是序列化的吗?
    • 你是对的,对于多个线程,复制操作将被序列化,因为每个线程在执行复制时都会持有全局锁。解决它的一种方法是将输入参数复制到局部变量中,并在不持有全局锁的情况下执行 memcpy。但是,您还需要注意其他几件事: a) 如何处理具有重叠 src/dest 范围的并行 memcpy? b) 你如何处理 memcpy 完成的通知,因为现在当每个 memcpy 完成时,你将有多个回调。
    • 由于现在您的问题范围更大,这些是您需要的构建块:a) 一个数据结构来维护当前正在执行的 memcpy 的范围。如果您检测到某个线程上的 memcpy 操作已发出到另一个线程当前正在读取的目标位置,则需要阻止新的 memcpy 操作以防止内存损坏。 b) 你需要使用 2 个多生产者/多消费者队列,一个用于提交复制操作,第二个用于提交完成通知 c) 一个线程组为输入队列中提交的操作提供服务。
    • 如果您需要任何帮助来构建基于这些原语的解决方案,请告诉我。
    【解决方案2】:

    不要挂起线程。那是糟糕的设计,很可能会给你带来麻烦。

    相反,可以这样想:让线程阻塞等待有关它应该做什么的信息。当它获得该信息时,它应该解除阻塞,完成工作,然后再次阻塞等待下一件事。

    快速搜索“C# 阻塞集合”会发现 BlockingCollection&lt;T&gt; 类,而这个 guide 用于取消其中一个阻塞操作。让它在线程退出时激活 CancellationToken,并在线程不工作时让线程等待阻塞操作。

    【讨论】:

    • 让我用“让线程块等待”来重新表述这个问题。
    猜你喜欢
    • 2012-08-16
    • 1970-01-01
    • 1970-01-01
    • 2011-05-31
    • 2019-03-26
    • 2015-02-12
    • 2016-06-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多