Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/137.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.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++ 避免多线程应用程序中潜在的死锁/内存泄漏_C++_Multithreading_Memory Leaks_Pthreads_Deadlock - Fatal编程技术网

C++ 避免多线程应用程序中潜在的死锁/内存泄漏

C++ 避免多线程应用程序中潜在的死锁/内存泄漏,c++,multithreading,memory-leaks,pthreads,deadlock,C++,Multithreading,Memory Leaks,Pthreads,Deadlock,简短版本: 如何处理产生一组线程的非原子性,在实现回调时运行一些自定义的未指定线程?下面介绍了几种可能的解决方案,似乎使用线程池是唯一好的解决方案。有没有标准的处理方法?无需发布完整的C++解决方案,伪代码或简短说明就足够了。性能是这里的一个重要方面 虽然这看起来很琐碎,但我相信下面的代码片段出现在许多现有的应用程序中,许多刚开始的应用程序,可能还有一些高级程序员可能编写类似的结构,甚至没有意识到其中的危险。pthread/C++11 std::thread/WinAPI和其他许多低级多线程库的

简短版本:

如何处理产生一组线程的非原子性,在实现回调时运行一些自定义的未指定线程?下面介绍了几种可能的解决方案,似乎使用线程池是唯一好的解决方案。有没有标准的处理方法?无需发布完整的C++解决方案,伪代码或简短说明就足够了。性能是这里的一个重要方面

虽然这看起来很琐碎,但我相信下面的代码片段出现在许多现有的应用程序中,许多刚开始的应用程序,可能还有一些高级程序员可能编写类似的结构,甚至没有意识到其中的危险。pthread/C++11 std::thread/WinAPI和其他许多低级多线程库的问题也是一样的。因此,这是一个重要的问题

长版本:

我正在设计一些多线程应用程序,并决定创建一个实用程序函数,在该函数中生成多个线程。这可能是一个非常常见的代码,它出现在我的许多应用程序中,除非它们使用OpenMP:

void ParallelCall(void (*function)(int, int), int numThreads)
{
    Thread *threads = new Thread[numThreads - 1];
    for(int i = 1; i < numThreads; ++ i) {
        if(threads[i - 1].start(&function, i, numThreads)) // this might fail
            abort(); // what now?
    }

    (*function)(0, numThreads);
    // use the calling thread as thread 0

    for(int i = 1; i < numThreads; ++ i)
        threads[i - 1].join();
    delete[] threads;
}
这更多的是一个用于说明问题的伪代码。正在创建和生成一组线程。Thread对象包装一个pthread线程。然后他们做了一些事情,最后他们加入了

现在的问题是:如果由于任何原因,某些线程无法启动可能是资源耗尽或每个用户的限制,该怎么办?我知道如何检测它的发生,但我不知道如何处理它

我想我应该等待成功启动的线程完成,然后抛出异常。但是,如果函数中的代码包含一些同步,例如屏障,这很容易导致死锁,因为其余的预期线程将永远不会生成

或者,我可以立即抛出一个异常,忽略正在运行的线程,但随后我会保留已分配的包装器对象,这会导致内存泄漏,也不会加入生成的线程

执行诸如杀死运行中的线程之类的操作似乎不是一个好主意坦率地说,我不太确定强制杀死多线程应用程序的线程会产生什么结果-似乎内存将处于未定义状态,这通常有点难以处理,如果回调函数分配内存,它本身可能会导致更多内存泄漏

在让所有线程进入回调函数之前,插入一个等待所有线程启动的过程,这在性能方面似乎是无法忍受的,尽管这样可以很容易地解决问题。另一种选择是拥有一个包含相关FIFO的派生线程池,等待任务,但我将派生的线程数量与逻辑CPU的数量相同,这是一个问题,但是如果numThreads更大呢?我将在代码中重新实现操作系统的调度程序


这通常是如何解决的?有更好的办法吗?如果不是,则取决于回调函数死锁中的内容是否比内存泄漏更好?

如何解决此问题:

以这样的方式创建每个线程:在允许它开始用户的工作函数之前,它会等待一个哨兵。您需要一个lambda来调用它 如果任何线程无法启动,请设置一个标志,指示现有线程应立即全部完成,而不是执行用户的功能。 在错误情况下,加入已启动的线程。然后以错误代码或异常退出,因为您希望异常更好

现在,您的函数是线程安全的,不会泄漏内存

编辑:这里有一些代码可以满足您的需要,包括一个测试。 如果要强制模拟失败的线程,请使用定义为1的Import_FAILURE重新编译

失败时的输出示例:

Compiling the source code....
$g++ -std=c++11 main.cpp -o demo -lm -pthread -lgmpxx -lgmp -lreadline 2>&1

Executing the program....
$demo 
initiating parallel call
Parallel call failed because: the test deliberately failed a thread: Cannot assign requested address

最后是一个请求——不要把你的图书馆放在世界上。std::thread库非常全面,如果这还不够的话,我们有OpenMP、TBB等。

让创建的线程在退出threadproc之前完成丢失的工作,以帮助解决问题,怎么样

List _StillBornWork;

void ParallelCall(void (*function)(int, int), int numThreads)
{
    Thread *threads = new Thread[numThreads - 1];
    for(int i = 1; i < numThreads; ++ i) {
        if(threads[i - 1].start(&function, i, numThreads)) {
            _StillBornWork.Push(i);
        }
    }

    (*function)(0, numThreads);
    // use the calling thread as thread 0

    for(int i = 1; i < numThreads; ++ i)
        threads[i - 1].join();
    delete[] threads;
}

ThreadProc(int i) {

  while(1) {
    do work

    // Here we see if there was any work that didn't get done because its thread
    // was stilborn.  In your case, the work is indicated by the integer i.
    // If we get work, loop again, else break.
    if (!_StillBornWork.Pop(&i))
      break;  // no more work that wasn't done.
  }

}

令人捧腹的你到底为什么要在世界上添加另一个线程库,并在中设计错误?让线程报告它们是活动的。。。只等待活着的。@πάνταῥεῖ 当然真正的问题是如何解决这一问题?而且这些事情在实践中很少发生,一个完全有效的解决方案也可能是终止应用程序。但是,从用户的角度来看,最好留下内存泄漏,指示应用程序退出(例如通过异常),并让它保存其状态,以便用户在重新启动后可以恢复。其他两个注释可能是正确的。。。但是如果你自己实现了,你能做什么呢?我想如果线程可以停止而不被杀死,那就太好了;异常可能携带对线程数组和
最后一个有效的线程,捕手可以处理它们。@RichardHodges,因为当然没有足够的线程。我相信很多程序员会经常编写类似的代码,甚至连想都不想,因此我相信这是一个有效的问题。。。很多代码。我真的很感激,虽然我在最初的问题中也提到了这种可能性,但从性能角度来看,它太贵了。至于最后的要求,我不能保证任何事情,我是一名计算机科学家,我有点像是为了开发新的和令人愤怒的东西而获得报酬。但我明白你的意思,否则我会百分之百地同意你的看法。:-多线程真的很难保证安全。它所需要的代码量常常让人们感到惊讶。问题在于,每增加一个线程,就会有无数的交叉案例呈指数增长。我知道这很难,我只是惊讶于你编写了这么多代码,却没有真正引入任何新的内容。不过,我真的很感激你的努力。嗯。。。引入的是防止死锁和内存泄漏,即使功能健壮。它需要大量的代码,因为它不是微不足道的。我希望你能从中获得一些见解。您是否仔细查看了代码并在自己的机器上编译和运行了它?不,我没有运行它,但我确实读过它。不幸的是,我忘了提到C++11是不可能的。您引入了我在原始问题中建议的实现,插入等待所有线程启动,然后再让它们进入回调-这就是您所做的,如果我错了,请纠正我。我不是一个初学编程的人,我自己也能写。毫无疑问,这不是一个简单的问题,尽管它看起来很琐碎,而且大多数其他读者很快就把它忽略掉了。我真的很感谢您的努力+1。这是一个有趣的解决方案,但仍然只适用于简单的情况,例如处理数组。对于某些算法,例如并行FFT,很难接受如果要求运行8个并发线程,则只有3个线程在运行,因为线程会在每个阶段结束时等待自己,以便能够启动下一个阶段-但如果只有3个,则该阶段永远不会结束。不过,这是一个有趣的工作。不知道你说的等待是什么意思。你能再解释一下吗?嗯,在函数内部的用户代码中,经常会有一些同步。如果计算分阶段进行,如FFT,请参阅,线程将包含同步点,例如,在其他线程完成当前阶段的处理之前,阻止更快线程进入下一阶段的障碍。如果不是所有预期线程都已启动并正在运行,此同步-此等待将使线程永远处于死锁状态。您可以添加此调用以检查屏障之前是否缺少工作。然而,您在这里主要做的是建模生产者-消费者工作队列。你有两种工作。一种是FFT。第二类是FFT级。将进行FFT的功添加到队列中。它生成第一阶段的工作,然后等待所有工作完成。当所有阶段完成时,FFT线程生成工作以执行下一阶段,等等。当然,您可以将单个阶段作为一个并行调用,将下一个状态作为另一个并行调用,中间的连接负责同步。但是,生成线程比同步线程花费的时间要多得多。而且,并不是每一种算法都像FFT那样简单,大多数算法都不像FFT那样简单,而且fork/join模型的使用也越来越少。这里的目标是提供一个本质上安全的并行调用,而不需要用户函数检查它通常不会检查的任何东西。
Compiling the source code....
$g++ -std=c++11 main.cpp -o demo -lm -pthread -lgmpxx -lgmp -lreadline 2>&1

Executing the program....
$demo 
initiating parallel call
Parallel call failed because: the test deliberately failed a thread: Cannot assign requested address
List _StillBornWork;

void ParallelCall(void (*function)(int, int), int numThreads)
{
    Thread *threads = new Thread[numThreads - 1];
    for(int i = 1; i < numThreads; ++ i) {
        if(threads[i - 1].start(&function, i, numThreads)) {
            _StillBornWork.Push(i);
        }
    }

    (*function)(0, numThreads);
    // use the calling thread as thread 0

    for(int i = 1; i < numThreads; ++ i)
        threads[i - 1].join();
    delete[] threads;
}

ThreadProc(int i) {

  while(1) {
    do work

    // Here we see if there was any work that didn't get done because its thread
    // was stilborn.  In your case, the work is indicated by the integer i.
    // If we get work, loop again, else break.
    if (!_StillBornWork.Pop(&i))
      break;  // no more work that wasn't done.
  }

}