【问题标题】:Concurrency with pthreads, a typical misbehaviour与 pthread 并发,典型的不当行为
【发布时间】:2023-04-04 13:00:01
【问题描述】:

我不确定下面的程序有什么问题,但它不会打印每种语言一次,而是随机地多一些,少一些,有些不会打印

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



char *messages[] = { 
    "English: Hello World!", 
    "French: Bonjour, le monde!",
    "Spanish: Hola al mundo", 
    "Klingon: Nuq neH!",
    "German: Guten Tag, Welt!", 
    "Russian: Zdravstvytye, mir!",
    "Japan: Sekai e konnichiwa!", 
    "Latin: Orbis, te saluto!"
};
#define NUM_MESSAGES (sizeof( messages ) / sizeof( char* ))


void *PrintHello( void *messageid )
{
    pthread_t taskid;
    int *id_ptr, message_num;

    taskid = pthread_self();
    printf( "This is thread with ID %lu.\n", taskid );

    message_num = *((int *) messageid);
    printf( "%s \n", messages[message_num] );

    pthread_exit( NULL );
}

int main( int argc, char *argv[] ) 
{
    pthread_t threads[NUM_MESSAGES];
    int rc, i;


    for( i = 0; i < NUM_MESSAGES; i++ ) {
        void * argument = (void*) &i;
        rc = pthread_create( &threads[i], NULL, PrintHello, argument );
        if( rc ) {
            printf( "ERROR; return code from pthread_create() is %d\n", rc );
            exit( -1 );
        }
    }




    for( i = 0; i < NUM_MESSAGES; i++ ) {
        pthread_join( threads[i], NULL );
    }

    pthread_exit( NULL );
}

我猜这个问题与参数指针有关。我试图锁定不同的部分,但没有成功。

【问题讨论】:

    标签: c concurrency pthreads


    【解决方案1】:

    您正在传递变量i 的地址,主线程不断变化。所以你受调度的摆布:有人会在你的线程有机会运行之前更改i 吗?

    相反,您可以尝试直接传递字符串,大大简化代码:

    rc = pthread_create( &threads[i], NULL, PrintHello, messages[i]);
    
    void *PrintHello(void *arg)
    {
        char *msg = arg;
        printf("%s\n", msg);
    
        return NULL;
    }
    

    还有另一种更糟糕的选择,您可以将 i 的实际值作为参数(而不是其地址)传递。

    【讨论】:

    • 感谢您的帮助。这对我有用。有没有尝试用锁来解决这个问题?我在设置“参数”之前尝试锁定并在创建线程后解锁,但这不起作用:/我知道这将是并发的想法,但只是为了我的兴趣:)
    • @smoes 不,锁定在这里是不可行的。您必须在创建线程之前以某种方式锁定,一旦您通过停止使用i 的点,就必须在线程中解锁。
    • @smoes,不使用 pthread 互斥锁 - pthread 互斥锁必须由锁定它们的同一线程释放,但在这里您正在等待另一个线程在解锁之前捕获该值。此外,只让一个线程一次运行,您将失去线程化的大部分好处。通常的解决方案是为您填写任何参数的每个线程分配一个新缓冲区,然后传递给 pthread_create。然后新线程负责释放缓冲区。这种方式不需要锁。
    【解决方案2】:

    这是典型的race 条件。
    确切的说,新线程创建后,线程的执行顺序决定了程序的结果。有两种情况:

      1234563
    1. 当新线程PrintHello 已创建但直到下一个i++main 线程中执行时才被调度。在这种情况下,您在线程函数中对argument 的引用,即message_num = *((int *) messageid); 并不是您所期望的。

    对此的解决方案是使用后在线程函数中动态分配(使用mallocargumentfree

    【讨论】:

      【解决方案3】:

      将标量整数参数传递给线程函数的一种方法是(ab-)使用 C99 标准第 6.3.2.3 节中的以下文本:

      (5) 整数可以转换为任何指针类型。除先前规定外, 结果是实现定义的,可能没有正确对齐,可能不指向 引用类型的实体,可能是陷阱表示。

      除了第二句的最后一部分,以下代码应该可以在大多数系统上运行:

      int int_val;
      ...
      pthread_create(..., ..., thread_func, (void *)int_val);
      

      然后在thread_func 中将其转换回整数:

      void *thread_func (void *data)
      {
          int int_arg = (int)data;
          ...
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-11-20
        • 2020-09-25
        • 1970-01-01
        • 1970-01-01
        • 2019-03-10
        • 1970-01-01
        相关资源
        最近更新 更多