【问题标题】:What is the flow of execution in Multi-threading?多线程中的执行流程是什么?
【发布时间】:2016-01-29 12:32:26
【问题描述】:

我在多线程中遇到问题。请考虑以下代码:

#include<stdio.h>
#include<pthread.h>

void* functionA(void*);
void* functionB(void*);


int main()
{   
    pthread_t tid[2];
    pthread_attr_t arg;

    for(int i = 0; i<2; ++i)
    {
        pthread_attr_init(&arg);

        if(i == 0)
        {
            int x = 0;  
            pthread_create(&tid[i], &arg, functionA, (void*)&x);
        }

        else if(i == 1)
        {
            int x = 6;  
            pthread_create(&tid[i], &arg, functionB, (void*)&x);
        }
    }

 // wait for both threads to finish execution...    
 pthread_join(tid[0], NULL);
 pthread_join(tid[1], NULL);

 return 0;
 }


//.........................DEFINATIONS........................

void* functionA(void* x)
{
    int Index = *((int*)x);
    printf("First: %d\n",Index);    //..................... LINE M
}


void* functionB(void* x)
{
    int Index = *((int*)x);
    printf("Second: %d\n",Index);   //....................... LINE N
}

现在我相信 functionA 将首先开始执行,因为当然 functionA 的线程将首先在 for 循环中创建,所以根据我的输出应该是:

  First: 0                         (from Line M)
  Second: 6                        (from Line N)

但实际输出是,

  Second: 6
  First: 6

现在看到这个我真的很惊讶,我不知道发生了什么。不仅 secondFunction 首先开始执行,而且两个函数都显示相同的值,即 6,这对我来说没有意义。谁能解释一下这里发生了什么???? 在此先感谢...

【问题讨论】:

  • x 在您创建线程后立即超出范围,因此您的代码显示 UB。
  • 尝试在您的线程中执行此操作:printf("%p\n", x);,我猜,您会看到,两个线程的指针是相同的,因为您的 int x = 0/6; 在本地范围内,因此它们可能会结束向上分配在同一地址。
  • 所以您正在使用线程,但您希望它们之间有特定的执行顺序?你认为启动线程和调用方法的区别是什么?
  • @Roman Hocke:是的,我明白你的意思了,谢谢 :)

标签: c multithreading pthreads


【解决方案1】:

两件事

1) 这里的 x 变量范围仅限于 if 块。所以在你的线程函数中,当你访问该指针时,它的作用域已经消失了所以你正在访问线程函数中的非法内存,这是错误的,它会产生未定义的行为。

回应您的一条评论

那么有什么方法可以代替变量,直接发送一个常数,例如 pthread_create(&tid[i], &arg, functionA, 6) ??

POSIX 线程是一个 C API。 C不提供语言设施,如 复制构造函数,因此无法复制任何对象 价值。

你只需要通过指针传递一些东西。

2) 线程执行的优先级完全取决于操作系统和调度,您不能假设这些线程的顺序。

您仍然希望线程之间进行一些同步,然后使用互斥锁、条件变量等。

【讨论】:

    【解决方案2】:

    无法保证它们将按什么顺序运行。创建线程时,操作系统的调度程序将注册新线程并在有时间时运行它。根据系统上的优先级和其他因素,它可能会以任何顺序选择它们,即中断主线程并启动其他线程之一,或者运行主线程直到加入,然后启动任何其他线程。

    您可能正在多核系统上进行测试。然后它们甚至可能同时并行运行,但由于某种原因,其中一个可能更快(也许来自第二个的数据在缓存中,而第一个必须从内存中获取数据?)

    长话短说:没有任何保证。

    如果您需要特定的顺序,您可以使用锁(即互斥锁)来强制同步,或者您可以设置优先级(参见 setpriority())或强制实时调度(sched_setscheduler()),但您确实应该了解您的操作系统。

    【讨论】:

      【解决方案3】:

      您将指向短期局部变量的指针传递到线程中,一旦这些范围退出,就会导致未定义的行为。可能两个线程看到x 的地址相同。

      最明显的解决方法是为变量使用更高的范围,并为每个线程创建一个包含整数的数组:

       pthread_t tid[2];
       pthread_attr_t arg;
       int x[2] = { 0, 6 };
       void * (*func[])(void *) = { functionA, functionB };
      
       for(int i = 0; i < 2; ++i)
       {
           pthread_attr_init(&arg);
           pthread_create(&tid[i], &arg, func[i], &x[i]);
       }
      
       // wait for both threads to finish execution...    
       pthread_join(tid[0], NULL);
       pthread_join(tid[1], NULL);
      

      这是有效的,因为 x 数组将在调用 pthread_join() 之后继续存在。也无需转换指针,int * 在 C 中自动转换为 void *

      你对线程启动顺序的假设也是错误的,没有这样的保证。

      【讨论】:

      • 那么有什么办法可以代替变量,直接发送一个常数,例如 pthread_create(&tid[i], &arg, functionA, 6) ??
      • @MuzahirHussain:您可以在启动线程之前malloc 变量的缓冲区,将指向该缓冲区的指针传递给线程并让线程本身free 缓冲区,一旦完成使用它。
      • 函数指针数组的声明不正确。这些函数返回void* 而不是void,因此声明需要类似于:void *(*func[])(void *) = { functionA, functionB };
      • @user3629249 谢谢,已修复!
      【解决方案4】:

      我相信 functionA 会首先开始执行,因为 当然,函数A的线程将首先在for循环中创建

      这个假设是不正确的,它依赖于操作系统控制的线程调度。

      如果您想看到正确的输出,请在 main() 函数范围内声明两个变量(在 for 循环之前)

      int x = 0;  
      int y = 6;  
      

      并且也做出改变

      pthread_create(&tid[i], &arg, functionA, (void*)&x);
       pthread_create(&tid[i], &arg, functionB, (void*)&y);
      

      通过这样做,您至少可以得到正确的输出。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多