如何不使用pthread

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何不使用pthread相关的知识,希望对你有一定的参考价值。

参考技术A 概述

Pthread是一套通用的线程库, 它广泛的被各种Unix所支持, 是由POSIX提出的. 因此, 它具有很好的可移植性.

例1:

/* ------ test.c ------- */
#include <pthread.h>
void *pp(void *arg)

printf("%s\n", (char *)arg);
sleep(2);

return NULL;


main()

printf("I am main thread\n");
sleep(1);



执行:
gcc test.c -lpthread
./a.out

输出:
I am main thread
hello world
I am main thread
hello world
............

二 返回值

也应该看到了, 每一个线程的返回值是void *.
有两种方法返回:
1 return pointer;
2 pthread_exit(pointer);
这两种方法是一样的.

那么, 其他的线程是如何得到这个返回值的呢?
用这个函数:
int pthread_join(pthread_t TH, void **thread_RETURN);

一个线程有两种状态, joinable 即系统保留线程的返回值, 直到有另外一个线程将它取走. detach系统不保留返回值.

下面的函数用于detach:
int pthread_detach (pthread_t TH);

pthread_t pthread_self(); 可以返回自己的id. 通常, 我们用下列的语句来detach自己:
pthread_detach(pthread_self());

三 Mutex

Mutex用于解决互斥问题. 一个Mutex是一个互斥装置, 用于保护临界区和共享内存. 它有两种状态locked, unlocked. 它不能同时被两个线程所拥有.

下面的函数用于处理Mutex:

初始化一个Mutex
int pthread_mutex_init (pthread_mutex_t *MUTEX, const pthread_mutexattr_t *MUTEXATTR);
锁定一个Mutex
int pthread_mutex_lock (pthread_mutex_t *mutex));
试图锁定一个Mutex
int pthread_mutex_trylock (pthread_mutex_t *MUTEX);
结锁一个Mutex
int pthread_mutex_unlock (pthread_mutex_t *MUTEX);
销毁一个Mutext
int pthread_mutex_destroy (pthread_mutex_t *MUTEX);

它的锁一共有三种: "fast", "recursive", or "error checking"
进行lock操作时:
如处于unlock状态, lock它, 即排斥占有。

在被其他线程lock的时候,
挂起当前线程, 直到被其他线程unlock

在已经被自己lock的时候,
"fast" 挂起当前线程.
"resursive" 成功并立刻返回当前被锁定的次数
"error checking" 立刻返回EDEADLK

进行unlock操作时:
解锁.
"fast" 唤醒第一个被锁定的线程
"recursive" 减少lock数(这个数仅仅是被自己lock的, 不关其它线程的) 当lock数等于零的
时候, 才被unlock并唤醒第一个被锁定的线程.
"error check" 会检查是不是自己lock的, 如果不是返回EPERM. 如果是唤 醒第一个被锁定的线程,

通常, 我们用一些静态变量来初始化mutex.
"fast" `PTHREAD_MUTEX_INITIALIZER'
"recursive" `PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP'
"error check" `PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP'

注意: _NP 表示no portable不可移植

例如:
// "fast" type mutex
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
... ...
pthread_mutext_lock(&mutex);
fwrite(buffer, 1, strlen(buffer), file);
pthread_mutex_unlock(&mutex);
... ...

四 Condition Variable (条件变量)

也是一种用于同步的device. 允许一个进程将自己挂起等待一个条件变量被改变状态.
有下列几个函数:

int pthread_cond_init (pthread_cond_t *COND,pthread_condattr_t *cond_ATTR);
int pthread_cond_signal (pthread_cond_t *COND);
int pthread_cond_broadcast (pthread_cond_t *COND);
int pthread_cond_wait (pthread_cond_t *COND, pthread_mutex_t *MUTEX);
int pthread_cond_timedwait (pthread_cond_t *COND, pthread_mutex_t *MUTEX, const struct timespec *ABSTIME);
int pthread_cond_destroy (pthread_cond_t *COND);

我想看看名字就可以知道它们的用途了. 通常我们也使用静态变量来初始化一个条件变量.
Example:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

pthread_cond_signal 用于唤醒一个被锁定的线程.
pthread_cond_broadcast 用于唤醒所有被锁定的线程.
pthread_cond_wait 用于等待.

为了解决竞争问题(即一个线程刚要去wait而另一个线程已经signal了), 它要与一个mutex连用.

看一看下面的例子:

int x,y;
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

//Waiting until X is greater than Y is performed as follows:

pthread_mutex_lock(&mut);
/**//* operate on x and y */
pthread_mutex_unlock(&mut);

pthread_cond_wait的执行过程如下:
1. 首先, 它unlock the mutex, then 挂起当前的线程.
2. 当被唤醒的时候, 它会lock the mutex.

这样就保证了这是一个临界区.

五 Thread-Specific Data (TSD)

说白了就是线程中使用的静态变量. 大家可以很容易的理解为什么使用静态变量函数不是线程安全的(也就是它们一定要很小心的在线程中使用).

而使用静态变量又是很方便的, 这就产生了 thread-specific data. 可以把它理解为一个指针数组, 但对于每个线程来说是唯一的.

Example:
int func()

char *p;
p = strdup(thread-specific-data[1]);
... ...


void *pthread-1(void *arg)

... ...
func()
... ...


void *pthread-2(void *arg)

... ...
func()
... ...


不同的线程调用func产生的结果是不同的. 这只是个例子.

int pthread_key_create(pthread_key_t *KEY, void (*destr_function) (void *));
int pthread_key_delete(pthread_key_t KEY);
int pthread_setspecific(pthread_key_t KEY, const void *POINTER);
void * pthread_getspecific(pthread_key_t KEY);

TSD可以看成是一个void *的数组.
注意: pthread_key_delete只是释放key占用的空间, 你仍然需要释放那个void *.

为了加深你的理解, 看一看下面的例子吧:

/* Key for the thread-specific buffer */
static pthread_key_t buffer_key;

/* Once-only initialisation of the key */
static pthread_once_t buffer_key_once = PTHREAD_ONCE_INIT;


pthread_once(&buffer_key_once, buffer_key_alloc);
pthread_setspecific(buffer_key, malloc(100));



return (char *) pthread_getspecific(buffer_key);



pthread_key_create(&buffer_key, buffer_destroy);



free(buf);

在不使用条件的情况下挂起 pthread

【中文标题】在不使用条件的情况下挂起 pthread【英文标题】:Suspend pthreads without using condition 【发布时间】:2010-06-29 13:05:52 【问题描述】:

我想暂停 pthreads,但显然没有 pthread_suspend 之类的功能。我在某处阅读了有关使用互斥锁和条件暂停 pthread 的信息,并将其用作以下内容:

#include <pthread.h>

class PThread 
public:
pthread_t myPthread;
pthread_mutex_t m_SuspendMutex;
pthread_cond_t m_ResumeCond;

void start() 
pthread_create(&myPthread, NULL, threadRun, (void*)this );


Thread()  

void suspendMe() 
pthread_cond_wait(&m_ResumeCond,&m_SuspendMutex);


void resume() 
pthread_cond_signal(&m_ResumeCond);

;

但我不明白为什么我们需要互斥锁和条件来挂起和恢复 pthread。不使用条件是否可以暂停和恢复?

【问题讨论】:

【参考方案1】:

您的代码不正确 - pthread_cond_wait() 要求在您调用互斥锁时已将其锁定:

void suspendMe()

    pthread_mutex_lock(&m_SuspendMutex);
    pthread_cond_wait(&m_ResumeCond, &m_SuspendMutex);
    pthread_mutex_unlock(&m_SuspendMutex);

但是,这仍然是错误的。线程可以以任何名称从pthread_cond_wait() 唤醒,不一定只有在收到信号时才必须。这意味着您需要将pthread_cond_wait() 与一些共享状态配对,该状态编码线程真正等待的条件 - 在最简单的情况下,您可以只使用标志变量。 pthread_cond_signal() 用于告诉线程它应该唤醒并重新检查共享状态。将此应用到您的实现中:

class PThread 
    public:

    pthread_t myPthread;
    bool suspended;
    pthread_mutex_t m_SuspendMutex;
    pthread_cond_t m_ResumeCond;

    void start() 
        suspended = false;
        pthread_create(&myPthread, NULL, threadRun, (void*)this );
    

    Thread()  

    void suspendMe() 
        pthread_mutex_lock(&m_SuspendMutex);
        suspended = true;
        do 
            pthread_cond_wait(&m_ResumeCond, &m_SuspendMutex);
         while (suspended);
        pthread_mutex_unlock(&m_SuspendMutex);
    

    void resume() 
        /* The shared state 'suspended' must be updated with the mutex held. */
        pthread_mutex_lock(&m_SuspendMutex);
        suspended = false;
        pthread_cond_signal(&m_ResumeCond);
        pthread_mutex_unlock(&m_SuspendMutex);
    
;

提供互斥锁的原因是为了保护共享状态并避免竞争条件 - pthread_cond_wait() 函数实际上在等待时执行原子解锁并等待,这可以避免“错过唤醒”。例如,在此代码中,互斥锁可防止 suspendedsuspended = true;pthread_cond_wait() 行之间更改为 false。

【讨论】:

另外,你需要在唤醒后检查你的状态,因为系统可能唤醒你的线程,条件为假!例如,系统可能已经唤醒了 10 个线程,其中一个已经完成了工作。 @Zan Lynx:是的,POSIX 在这一点上非常清楚:“可能会发生来自 pthread_cond_timedwait() 或 pthread_cond_wait() 函数的虚假唤醒。”(尽管我相信Linux 实现不会进行虚假唤醒)。 所以pthread_cond_wait 就像一个非常松散的狗链。你的比特犬随时都可能开始咬邻居。 不,它更像是商店柜台上的铃铛。如果你按铃,店主肯定会来柜台问你要什么,但如果你不按,他们可能会来。【参考方案2】:

如果一个线程没有在某些条件下等待,你怎么能“发出信号”让它恢复。它不能只是停止执行任何事情然后神奇地重新开始,所以它会等待一个条件。

详细地说,在 pthreads 中,恢复线程的方法实际上是使用条件变量。没有可用于以任何其他方式暂停/恢复线程的 API。等待pthread_cond_wait 很便宜,它会阻塞直到发出条件信号,而不使用(很多?)CPU。您使用条件来通知线程唤醒,并且需要互斥锁来保护唤醒时对条件变量和线程中代码的访问。

【讨论】:

"你怎么能“发出信号”让它恢复?"在其他 API 中是可能的!例如,Windows API 中的ResumeThread 通过接受整数线程 ID 来工作。还有一个SuspendThread 函数。当工作可用或完成时,您可以一起轻松地唤醒或挂起线程池中的工作线程(从主线程)。 There is a nice paragraph here 描述了同样的事情。 @bobobobo 您是否阅读了这些功能的文档?它们不是用于线程同步,而是用于调试器。 pthreads 是一个提供线程功能的库,包括同步。它不是用于实现调试器的库,因此它没有设计用于调试器的等效函数 来自文档:“这个函数主要是为调试器设计的。它不打算用于线程同步。在拥有同步对象的线程上调用 SuspendThread,例如互斥锁或如果调用线程试图获取挂起线程拥有的同步对象,则可能导致死锁。为避免这种情况,应用程序中的非调试器线程应向其他线程发出信号以挂起自身。目标线程必须设计为监视此信号并做出适当的响应。” 其实是的,我昨天刚做了,我意识到我错了。我决定对此事保持沉默,但你指出了这一点,这很好。【参考方案3】:

条件总是与互斥锁相关联。通常,线程会休眠,因为它正在等待状态更改以指示它有工作要做。您需要互斥锁来保护对该状态的访问,以及发出更改信号的条件。

唤醒一个线程而不告诉它你为什么唤醒它是一件很奇怪的事情,所以没有特殊的方法可以做到;唯一的方法是使用正常机制,但没有共享状态。

如果出于某种原因你想暂停和恢复另一个线程的线程,独立于让它工作,那么你可以使用pthread_kill 发送SIGSTOPSIGCONT 信号;我从来没有尝试过这样做,所以我不知道它是否受支持。

【讨论】:

【参考方案4】:

互斥锁用于确保独占访问,而条件变量用于根据事件同步线程。

我们需要互斥锁来确保条件变量不会无限等待。 要记住的一件事是锁定和解锁的互斥操作保证是原子的,但条件变量不必是原子的。即当条件变量等待中途时,线程可以被调度。

考虑以下没有 Mutex 作为条件变量的情况。

线程 1

1)执行一些操作 2)等待条件变量 3)继续操作

线程 2

1) 执行一些操作 2) 给条件变量发信号 3)继续操作

在线程 1 中,步骤 2 不能保证是原子的。如果线程 1 在完成 step1 之前被调度程序推出 RUNNING 状态。 现在线程 2 开始执行并向条件变量发出信号。当线程 1 恢复执行时,它将完成剩余的低级指令并开始等待。 线程 1 以无限等待结束,因为条件变量的信号甚至在等待之前就出现了。

所以正确的使用方法是(我相信问题中提到的代码没有达到预期的效果)

线程 1:-

1)工作到一定条件必须发生的地步(比如“count”必须达到指定值) 2)锁定关联互斥体 3) 调用 pthread_cond_wait() 以执行来自 Thread1 的阻塞等待信号。 (请注意,对 pthread_cond_wait() 的调用会自动以原子方式解锁关联的互斥变量,以便 Thread2 可以使用它) 4) 收到信号后,醒来。互斥锁是自动和原子锁定的。 5)显式解锁互斥锁

线程2

1) 工作 2)锁定关联互斥体 3) 更改 Thread1 正在等待的全局变量的值。 4)检查全局 Thread1 等待变量的值。如果它满足所需条件,则发出 Thread1 信号。 5)解锁互斥锁。 继续

【讨论】:

【参考方案5】:

看起来没有任何 Linux 替代 Windows API SuspendThread 函数。如果不在该线程过程中注入任何代码,就不可能暂停 Linux 线程。

【讨论】:

SuspendThread 在用户模式代码中总是很臭。甚至文档都说“这个函数主要是为调试器设计的。它不打算用于线程同步。”,这对于任何想要使用它的人来说应该足够了。【参考方案6】:

更重要的是 - 你最终想要做什么? - 我怀疑答案不是“暂停线程”。可能是你的程序设计有问题。

【讨论】:

以上是关于如何不使用pthread的主要内容,如果未能解决你的问题,请参考以下文章

Linux多线程

C 语言编程 — pthread 线程操作

如何设置相机视图旋转(不使用不推荐使用的代码)

我想反转堆栈,但我不知道如何使用递归来反转这个......如何在不使用递归的情况下反转堆栈

如何实际使用使用不和谐 oauth2 请求的数据

如何使用随机值生成数组,而不使用循环?