如何安全地操作线程中的参数,使用C ++&并行线程?

时间:2010-06-09 18:03:20

标签: c++ memory locking mutex pthreads

我在使用pthreads的程序遇到一些问题,其中发生了偶然的崩溃,这可能与线程如何对数据进行操作有关

所以我对如何使用线程和内存布局进行编程有一些基本问题:

假设公共类函数对某些字符串执行某些操作,并将结果作为字符串返回。该函数的原型可能是这样的:

std::string SomeClass::somefunc(const std::string &strOne, const std::string &strTwo)
{
 //Error checking of strings have been omitted
 std::string result = strOne.substr(0,5) + strTwo.substr(0,5);
 return result;
}
  1. 假设作为动态的字符串存储在堆上,但是在运行时在堆栈上分配对字符串的引用是正确的吗?
  2.   

    Stack:[some mem addr]指针地址   到字符串在堆上的位置

         堆:[some mem addr]内存   分配给初始字符串   可能会成长或缩小

    为了使函数线程安全,使用以下互斥锁(在“SomeClass”中声明为private)锁定函数:

    std::string SomeClass::somefunc(const std::string &strOne, const std::string &strTwo)
    {
     pthread_mutex_lock(&someclasslock);
    
     //Error checking of strings have been omitted
     std::string result = strOne.substr(0,5) + strTwo.substr(0,5);
    
     pthread_mutex_unlock(&someclasslock); 
    
     return result;
    }
    
    1. 这是一种安全的方法来锁定对字符串执行的操作(全部三种),或者在下列情况下调度程序可以停止某个线程,我认为这会使预期陷入困境逻辑:

      一个。在函数调用之后,参数:strOne& strTwo已经设置在函数在堆栈上的两个引用指针中,调度程序占用了线程的处理时间并允许新的线程进入,这会覆盖函数的引用指针,然后再由调度程序停止,让第一个线程重新进入?

      湾使用“result”字符串也会出现同样的情况:第一个字符串构建结果,解锁互斥锁,但在返回之前,调度程序允许另一个执行所有工作的线程,覆盖结果等。

    2. 或者,当另一个线程正在执行它的任务时,参考参数/结果字符串是否被压入堆栈?

      1. 这是在线程中执行此操作的安全/正确方法,并“返回”结果,以传递对将填充结果的字符串的引用:

        void SomeClass :: somefunc(const std :: string& strOne,const std :: string& strTwo,std :: string result) {  的pthread_mutex_lock(安培; someclasslock);

        //省略了对字符串的错误检查  result = strOne.substr(0,5)+ strTwo.substr(0,5);

        调用pthread_mutex_unlock(安培; someclasslock); }

      2. 预期的逻辑是“SomeClass”类的几个对象创建新线程并将自身对象作为参数传递,然后调用函数:“someFunc”:

        int SomeClass::startNewThread()
        {
        
         pthread_attr_t attr;
         pthread_t pThreadID;
        
         if(pthread_attr_init(&attr) != 0)
          return -1;
        
         if(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0)
          return -2;
        
         if(pthread_create(&pThreadID, &attr, proxyThreadFunc, this) != 0)
          return -3;
        
         if(pthread_attr_destroy(&attr) != 0)
          return -4;
        
         return 0;
        }
        
        void* proxyThreadFunc(void* someClassObjPtr)
        {
         return static_cast<SomeClass*> (someClassObjPtr)->somefunc("long string","long string");
        }
        

        很抱歉这么长的说明。但我希望问题和预期目的是明确的,如果不让我知道,我会详细说明。

        祝你好运。 克里斯

2 个答案:

答案 0 :(得分:1)

1 a / b:不,都不会发生。函数的参数及其返回值位于堆栈上,每个线程都有自己的堆栈。但是,其他事情肯定会出错:

  • 其中一个字符串操作可能会抛出异常,从而阻止某些类锁定解锁并且您的应用程序将挂起。
  • 假设传递给函数的字符串在线程之间共享(如果不是,则不需要锁定),另一个线程可以在调用函数之后和获取锁之前调用它们上的析构函数。在这种情况下,字符串操作将导致未定义的行为。

我建议您为每个线程创建一个新的SomeClass对象。在这种情况下,这些对象的所有成员只能由一个线程访问,并且不需要通过锁保护。 缺点是,在启动新线程后,您无法再从主线程访问它们。如果需要,那么你必须用锁来保护它们(锁也是该类的成员)。

话虽如此,函数somefunc似乎根本不会影响对象的任何成员,因此不需要保护。想想线程之间共享的粒度,我认为保护锁应该在调用somefunc的函数中。

答案 1 :(得分:0)

一般建议:尽量减少可能发生对共享数据的访问的位置。共享数据是指任何线程都可以随时访问的数据。

有一些处理多线程编程的一般方法:

Producer consumer

Reader writers

当然还有其他方式,但这两种方式最常用 - 至少是我(特别是第一种)。