C P线程数

C Pthreads numbers

我是 C pthreads 的新手,我遇到了一些小问题。我想制作一个在我的 var 结果 = 13 时停止的程序。我有两个线程在我的结果中添加或减去一个随机数。 问题是,在编译的情况下,减法线程从不工作。 Result、Step 和 Mutex 是我的全局变量。感谢大家

void add(){
int random;
while(result < 101){
    random = rand() % 51;
    pthread_mutex_lock(&mutex);
    result += random;
    step += 1;
    pthread_mutex_unlock(&mutex);
    printf("%d. [random -> %d, result now -> %d] ADD\n",step,random,result);

}
pthread_exit(0);


void sub(){
int random;
while(result > 5){
    random = rand() % 51;
    pthread_mutex_lock(&mutex);
    result -= random;
    step +=1;
    pthread_mutex_unlock(&mutex);
    printf("%d. [random -> %d, result now -> %d] SUB\n",step,random,result);

}
pthread_exit(0);

void main(int argc, char**argv){

pthread_t th1;
pthread_t th2;

pthread_mutex_init(&mutex,NULL);

if(pthread_create(&th1, NULL, add, NULL)!= 0)
    printf("ERROR: Thread 1 not created");

if(pthread_create(&th2, NULL, sub,NULL)!=0)
    printf("ERROR: Thread 2 not created");

while(result != 13){

}
pthread_cancel(th1);
pthread_cancel(th2);

pthread_exit(0);

您有锁饥饿问题。线程在临界区外执行操作的时间太短,并且调度程序在另一个拥有锁的情况下尝试重新调度线程的概率太高。

您可以通过以下方式更改操作:

  • 增加临界区外的时间

    本例假设您可以为线程找到一些任务,该任务足够长以使其在一段时间内保持忙碌。虽然这是对 CPU 资源的浪费。

  • 强制重新安排pthread_yield()

    这将强制调度程序在线程离开临界区后以或多或少的 RR 方式在线程之间切换。这可能会或可能不会满足您的需求,因为在实际应用程序线程中通常表现得不太确定。

  • 使用一些随机延迟 sleep() 或等价物

    这是一种组合方法,因此线程在临界区之外有一些随机延迟,模拟一些工作量,但没有加载 CPU。同样,这种方法有一些局限性,就像在现实生活中你有很多线程并且 CPU 没有可用容量一样,系统可能会有不同的行为。

这里有很多错误:


rand 不是线程安全的,因此应该将其移动到受互斥锁保护的临界区。


您的线程应该执行 while(true) 结合 pthread_yield/sched_yield 而不是 while(result > 5)while(result < 101):

while (true) {
    if (result >= 101) {
        pthread_yield();
        continue;
    }
    ...

否则您的线程可能会提前停止。例如。假设 result100 并且添加函数将 42 添加到它,下一次迭代将停止该线程。


一般来说 pthread_cancelpthread_join 结合使用。

使用上面讨论的解决方案,pthread_cancel 可能需要一段时间才能完成,因为 printf 不需要作为取消点,线程只能在此类取消点上取消。另见 here(取消点和 )。

所以你可以做的是添加一个布尔参数 stop_thread 并且如果为真则停止 while 循环并退出循环。 现在,如果 result13,则在线程上将 main 中的 stop_thread 设置为 truepthread_join。 加入后 result 可能会改变。 如果 result 不应更改,那么您应该在线程本身中检查 13


你的函数签名是错误的。 pthread_create 预计 void *(*start_routine)(void*)。 所以把你的函数改成

void *add(void*) {
    ...
    return NULL; /* no pthread_exit needed */
}

有了这样的签名,甚至可以将参数交给线程而不是依赖全局变量。


无需在main中调用pthread_exit


即使进行了所有这些更改,程序也不太可能完成。 main 必须在 result13 时准确安排,这不太可能,因为在线程中 13 的情况下没有取消点,没有收益等。 因此,您最有可能做的是检查线程本身 if result == 13 并停止它们。 不过,您必须确保没有其他正在等待互斥体的线程会修改 result

你还应该在main中的循环中添加一个pthread_yield,以确保其他线程将运行。

除了一些 "smaller" 问题,例如 运行domly 放置的 pthread_exit() 语句,线程函数的错误签名(它们必须是 void* f(void*) ,您的 while 循环在 add() 和 sub() 以及其他一些东西中的错误选择,您的代码看起来不错。

您可能知道,您的代码不能保证运行永远终止,因为它赌 main() 曾经碰巧探测结果的值为 13,当它具有该值时。尽管它会终止(总有一天),但可能性很大。

当我 运行 它时,程序通常在 2000 年的 运行ge 中以 stepAdd,stepSub 终止。运行 它使用 4 核 freebsd VM。

您的印象是您的子线程永远不会运行,因为您的 printf 语句造成了这种印象。如果时间片是 100 毫秒,你会得到大量的 printf 输出,甚至你的控制台缓冲区或你使用的任何东西都会被填满。

我对使用 POSIX 线程的建议是花费少量的额外时间来确保默认值不会为您带来一些意想不到的惊喜。

例如,pthread_mutex_init() 默认创建非递归互斥体。这意味着,如果您(无意或有意)编写 lock() lock(),线程将自行死锁。虽然可以争论,这是一个 "feature",但我倾向于说它会在更大的系统中产生问题,一旦函数调用树变得更大一些。

另一个示例可能是线程的默认堆栈大小。它在某些系统上非常小。

在我的示例中,我确定调度程序模式是 SCHED_RR(我认为这是大多数系统的默认设置,但我也看到默认设置为 SCHED_FIFO 的系统) .

将 printf 移至 main() 以便能够查看两个线程函数的步长值。

添加了一些更简洁、更真实的关机代码。

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

typedef void*(*ThreadFunc)(void*);

static volatile int running = 1;
static volatile int result = 0;
static pthread_mutex_t mtx1;
static volatile int stepAdd = 0;
static volatile int stepSub = 0;

void* add( void *)
{
    int random;

    while( running )
    {
        if( result < 101 )
        {
            pthread_mutex_lock(&mtx1);
            random = rand() % 51;
            result += random;
            stepAdd += 1;
            pthread_mutex_unlock(&mtx1);
            //printf("%d. [random -> %d, result now -> %d] ADD\n",step,random,result);
        }
        // else - one could avoid some useless spinning of the thread. (pthread_yield())
    }
    pthread_exit(NULL);
}

void* sub( void *)
{
    int random;
    while( running )
    {
        if( result > 5 )
        {
            pthread_mutex_lock(&mtx1);
            random = rand() % 51;
            result -= random;
            stepSub += 1;
            pthread_mutex_unlock(&mtx1);
//          printf("%d. [random -> %d, result now -> %d] ADD\n",step,random,result);
        }
    }
    pthread_exit(NULL);
}



static int RunThread(pthread_t *handle, ThreadFunc f, void* context )
{
    int result;
    pthread_attr_t thread_attribute;
    pthread_attr_init(&thread_attribute);
    pthread_attr_setschedpolicy(&thread_attribute,SCHED_RR);
    result = pthread_create(handle,&thread_attribute, f, context );
    pthread_attr_destroy(&thread_attribute);
    return result;
}

int main(int argc, const char * argv[])
{
    void *addResult = NULL;
    void *subResult = NULL;
    pthread_t addThread = 0;
    pthread_t subThread = 0;
    // Default mutex behavior sucks, btw.
    // It can auto-deadlock as it is not recursive.
    pthread_mutex_init(&mtx1,NULL); 
    RunThread(&addThread, add, NULL );
    RunThread(&subThread, sub, NULL );

    while( running )
    {
        if( 13 == result )
        {
            running = 0;
        }
        else
        {
            printf("Steps: add(%d) sub(%d) -- result = %d\n", stepAdd, stepSub, result );
        }
    }

    pthread_join( addThread, &addResult );
    pthread_join( subThread, &subResult );

    puts( "All done - bye." );

    return 0;
}
// compiled with:
// clang++ -std=c++11 -stdlib=libc++ -lpthread baby_threading.cpp
// but should compile as C as well.