プロデューサとコンシューマのスレッド ループ


ここに画像の説明を挿入します

参考

参考:スレッドセーフなキュー

知らせ

pthread_mutex_t ミューテックス ロック

  • を使用しPTHREAD_MUTEX_INITIALIZERて初期化された場合、デフォルトの状態はロック解除されています (つまり、ロック解除された状態)このメソッドは、静的に初期化されたミューテックス ロックを作成します。これは、静的グローバル変数または静的ローカル変数の初期化に適しています。
  pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;			//未锁定的(即解锁状态)
  • pthread_mutex_init初期化に関数を使用する場合、属性パラメータを指定することで初期状態を決定できます。属性パラメータが の場合、ミューテックスはNULLデフォルトでロック解除されます。ロックされた状態で作成する必要がある場合は、pthread_mutexattr_settype関数を使用してプロパティを に設定できますPTHREAD_MUTEX_INITIALIZER

    pthread_mutex_t mutex;
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_INITIALIZER); // 设置属性为初始化器;指定互斥锁的初始化状态为锁定状态
    
    pthread_mutex_init(&mutex, &attr); // 初始化互斥锁;锁定状态的互斥锁
    

pthread_cond_t 条件変数

  • を使用しPTHREAD_COND_INITIALIZERて初期化すると、デフォルトの状態は起動済みです。このメソッドは、静的に初期化された条件変数を作成します。これは、静的グローバル変数または静的ローカル変数の初期化に適しています。サンプルコードではcondを使用して初期化されているPTHREAD_COND_INITIALIZERため、作成時にすでに起動されています。

    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    
  • 関数を使用しpthread_cond_initて初期化された場合、条件変数はデフォルトでは起動されません作成時に起動する必要がある場合は、初期化前にpthread_cond_signalまたはpthread_cond_broadcast関数を手動で呼び出すことができます。

    pthread_cond_t cond;
    pthread_cond_init(&cond, NULL);
    

ミューテックスと条件変数を使用する場合は、正しい初期化状態を確保することが重要です。使用前に初期化しないと、未定義の動作やエラーが発生する可能性があります。したがって、ミューテックスと条件変数は使用前に常に明示的に初期化することをお勧めします。

スレッド セーフティ、プロデューサ、コンシューマ モデル

条件変数 + ミューテックス ロックを使用して、プロデューサー スレッドとコンシューマー スレッドの周期的実行の効果を実現します。

#include<stdio.h>
#include<iostream>
#include<queue>
#include<unistd.h>
#include<pthread.h>

using namespace std;
int flags = 0;
pthread_cond_t cond;                  //条件变量
pthread_mutex_t mutex;              //互斥锁
//生产者
void *product_thread_start(void *arg)
{
    
    
    while (1)
    {
    
    
        /*flags:定义flags的目的就是为了确定生产者和消费者的执行的顺序
        原因是如果不定义这个flags,生产者和消费者都要抢占这个锁,不一定
        谁抢占成功,加上flags之后就能够确定生产者先执行

        为什么要 while (flags != 0)而不使用 if (flags != 0)
        如果生产者第一次抢占到锁,第二次还是生产者抢占到锁
        第二次执行pthread_cond_wait会立即退出,原因上一次它执行了pthread_cond_signal
        但是由于while是个循环flags的值没有改,锁它会第二次执行pthread_cond_wait休眠
        直到消费者线程执行一次*/
        pthread_mutex_lock(&mutex);
        while (flags != 0){
    
    
            cout<<"I am product: " << pthread_self()
                << "  flags == " << flags
                << "  LINE="<<__LINE__<<" [生产者等待]"<<endl;

            pthread_cond_wait(&cond, &mutex);           //flags == 1等待

            cout<<"I am product: " << pthread_self()
                << "  flags == " << flags
                << "  LINE="<<__LINE__<<" [开启生产]"<<endl;
        }

        flags = 1;
        sleep(1);
        printf("生产了一辆超级跑车\n");
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mutex);
        cout<<" [生产完]"<<endl;
        cout<<endl;
    }
    pthread_exit(NULL);
}

//消费者
void *consume_thread_start(void *arg)
{
    
    
    while (1)
    {
    
    
        pthread_mutex_lock(&mutex);
        while (flags == 0){
    
    
            cout<<"I am consume: " << pthread_self()
                << "  flags == " << flags
                << "  LINE="<<__LINE__<<"[消费者等待]"<<endl;

            pthread_cond_wait(&cond, &mutex);

            cout<<"I am consume: " << pthread_self()
                << "  flags == " << flags
                << "  LINE="<<__LINE__<<" [开启消费]"<<endl;
        }
        flags = 0;
        sleep(1);
        printf("我购买了一辆超级跑车\n");
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mutex);
        cout<<" [消费完]"<<endl;
        cout<<endl;
    }
    pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
    
    
    pthread_t product_tid, consume_tid;

    pthread_mutex_init(&mutex, NULL);       //默认状态是未锁定的(即解锁状态)
    pthread_cond_init(&cond, NULL);           //默认情况下条件变量是未被唤醒的

    if (pthread_create(&product_tid, NULL, product_thread_start, NULL))
        perror("create tid1 error");
    if (pthread_create(&consume_tid, NULL, consume_thread_start, NULL))
        perror("create tid1 error");

    pthread_join(product_tid, NULL);
    pthread_join(consume_tid, NULL);
    printf("product_tid=%#lx,consume_tid=%#lx\n", product_tid, consume_tid);
    pthread_cond_destroy(&cond);
    pthread_mutex_destroy(&mutex);

    return 0;
}

可能性 1:

  • コンシューマは初めてロックを獲得し、待機に入ります。
  • 「生産の終了」後にプロデューサーが目覚めるとpthread_cond_signal(&cond);、プロデューサーは再びロックを取得して待機に入ります。
  • 消費者が「消費を終了」して目覚めた後pthread_cond_signal(&cond);、消費者は再びロックを獲得して待機に入ります。

ここに画像の説明を挿入します

可能性 2:

  • プロデューサーは初めてロックを獲得し、本番環境に入ります。
  • 「生産の終了」後にプロデューサーが目覚めるとpthread_cond_signal(&cond);、プロデューサーは再びロックを取得して待機に入ります。
  • 消費者が「消費を終了」して目覚めた後pthread_cond_signal(&cond);、消費者は再びロックを獲得して待機に入ります。

ここに画像の説明を挿入します

スレッドセーフなキュー

参考

参考:スレッドセーフなキュー

#include<stdio.h>
#include<iostream>
#include<queue>
#include<unistd.h>
#include<pthread.h>
using namespace std;
#define THREAD_COUNT 1              //生产者和消费者数量

//创建线程安全队列
class RingQueue{
    
    
public:
    RingQueue(){
    
    
        capacity = 1;
        pthread_mutex_init(&que_lock, NULL);
        pthread_cond_init(&consum_cond, NULL);
        pthread_cond_init(&product_cond, NULL);
    }
    ~RingQueue(){
    
    
        pthread_mutex_destroy(&que_lock);
        pthread_cond_destroy(&consum_cond);
        pthread_cond_destroy(&product_cond);
    }

    //往队列中放数据,生产
    void Push(int data){
    
    
        pthread_mutex_lock(&que_lock);
        while(que.size()>=capacity){
    
    
            cout<<"  que.size() == " << que.size()
               << " LINE="<<__LINE__<<" [生产者等待]"<<endl;

            pthread_cond_wait(&product_cond, &que_lock);

            /*为什么要用while循环呢?
        因为当生产者被唤醒后,需要再次判断队列是否可以满足生产的条件
        生产者或者消费者都是需要在等待结束后再次判断的*/
            cout<< "  que.size() == " << que.size()
                << "  LINE="<<__LINE__<<" [开启生产]"<<endl;
        }
        que.push(data);                           //生产,往队列中放入数据

        cout<<"I am product: " << pthread_self()
           << "I product number is " << data << endl;

        pthread_mutex_unlock(&que_lock);
        //生产者完成生产后唤醒消费者线程让消费者进行消费
        pthread_cond_signal(&consum_cond);
        cout<<endl;
    }

    //从队列中取数据,消费
    int Pop(){
    
    
        pthread_mutex_lock(&que_lock);
        while(que.size() <= 0){
    
    
            cout<< "  que.size() == " << que.size()
                << "  LINE="<<__LINE__<<" [消费者等待]"<<endl;

            pthread_cond_wait(&consum_cond, &que_lock);

            cout<< "  que.size() == " << que.size()
                << "  LINE="<<__LINE__<<" [开启消费]"<<endl;
        }
        int data = que.front();
        que.pop();

        cout<<"I am consume: " << pthread_self()
           << "I consume number is " << data << endl;

        pthread_mutex_unlock(&que_lock);
        //消费者线程消费之后通知生产者来生产
        pthread_cond_signal(&product_cond);

        cout<<endl;
        return data;
    }

private:
    queue<int> que;            //线程安全的队列
    //给队列一把锁,保证互斥,保证同一时刻只有一个线程对队列进行操作
    pthread_mutex_t que_lock;

    /*同步的条件变量,队列有元素,消息,没有元素等待,唤醒生产者
  保证生产者在队列中没有元素的时候进行生产(插入元素)*/
    pthread_cond_t consum_cond;
    pthread_cond_t product_cond;
    int capacity;               //队列容量,队列元素大于容量表示队满,不再往里插入元素
};

int g_val = 0;
//静态初始化保护g_val的互斥锁;默认状态是未锁定的(即解锁状态)
pthread_mutex_t g_val_lock = PTHREAD_MUTEX_INITIALIZER; //互斥锁

//生产者
void* product_thread_start(void* arg){
    
    
    RingQueue *q = (RingQueue*)arg;
    while(1){
    
    
        pthread_mutex_lock(&g_val_lock);//获取g_val的互斥锁
        q->Push(g_val);
        g_val++;
        sleep(1);
        pthread_mutex_unlock(&g_val_lock);
    }
}

//消费者
void* consum_thread_start(void* arg){
    
    
    RingQueue *q = (RingQueue*)arg;
    while(1){
    
    
        q->Pop();
    }
}

int main(){
    
    
    pthread_t consum_tid[THREAD_COUNT];
    pthread_t product_tid[THREAD_COUNT];
    RingQueue* q = new RingQueue();
    for(int i=0; i<THREAD_COUNT; ++i){
    
    
        int ret = pthread_create(&consum_tid[i], NULL, consum_thread_start, (void*)q);
        if(ret < 0){
    
    
            perror("pthread_create");
            return 0;
        }
        ret = pthread_create(&product_tid[i], NULL, product_thread_start, (void*)q);
        if(ret < 0){
    
    
            perror("pthread_create");
            return 0;
        }
    }
    for(int i=0; i<THREAD_COUNT; ++i){
    
    
        pthread_join(consum_tid[i], NULL);
        pthread_join(product_tid[i], NULL);
    }
    delete q;
    return 0;
}

可能性 1:

  • プロデューサーは初めてロックを獲得し、本番環境に入ります。
  • 消費者が「消費を終了」して目覚めた後pthread_cond_signal(&product_cond);、消費者は再びロックを獲得して待機に入ります。
    ここに画像の説明を挿入します

可能性 2:

  • コンシューマは初めてロックを獲得し、待機に入ります。
  • 消費者が「消費を終了」して目覚めた後pthread_cond_signal(&product_cond);、消費者は再びロックを獲得して待機に入ります。

ここに画像の説明を挿入します

スレッドミューテックスAPI

  1. ミューテックスロックを定義する変数
pthread_mutex_t mutex;
  1. スレッドのミューテックスロックを初期化します
int pthread_mutex_init(pthread_mutex_t * mutex, const pthread_mutexattr_t * attr); //动态初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //静态初始化;未锁定的(即解锁状态)
功能:初始化线程的互斥锁
参数:
    @mutex:互斥锁变量的地址
    @attr:缺省属性,默认填写为NULL; 未锁定的(即解锁状态)
返回值:成功返回0,失败返回错误码
  1. ロックされています
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex); 	//如果获取不到资源,不会阻塞,立即返回
功能:上锁(如果互斥锁可用直接能占用锁,如果互斥锁不可用将会阻塞等待)
参数:
    @mutex:互斥锁变量的地址
返回值:成功返回0,失败返回错误码
  1. ロックを解除する
int pthread_mutex_unlock(pthread_mutex_t *mutex);
功能:解锁
参数:
    @mutex:互斥锁变量的地址
返回值:成功返回0,失败返回错误码 
  1. ミューテックスロックを破棄する

    int pthread_mutex_destroy(pthread_mutex_t *mutex);
    功能:销毁互斥锁
    参数:
        @mutex:互斥锁变量的地址
    返回值:成功返回0,失败返回错误码
    

スレッド同期: 条件変数用の API

  1. 条件変数を定義する

    pthread_cond_t cond;
    
  2. 条件変数の初期化

    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;			//静态初始化 //已经被唤醒的
    int pthread_cond_init(pthread_cond_t *restrict cond,  const pthread_condattr_t *restrict attr);
    功能:动态初始化一个条件变量
    参数:
       @cond	:	条件变量的指针
       @attr	:    	NULL使用默认属性  ;未被唤醒的
    返回值:成功返回0,失败返回非0
    
  3. ブロッキング待ち条件変数

    int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
    功能:阻塞等待条件变量,在条件变量中维护了一个队列,
          这里的互斥锁就是为了解决在往队列中放线程的时候出现竞态问题的。
    参数:
        @cond	:条件变量的地址
        @mutex	:互斥锁
    返回值:成功返回0,失败返回非零
    

    実装手順:
    1. pthread_mutex_lock を使用してロックする
    2. pthread_cond_wait を呼び出す
    2.1 現在のスレッドをキューに入れる
    2.2 ロックを解除する ------------------------ --------同じロックを使用するスレッドはロックを取得できます
    2.3 スリープ ----------------------------- -- ---待機中
    2.4 ロックの取得
    2.5 スリープからの終了
    3. スレッドがプログラムを実行する
    4. pthread_mutex_unlock を使用してロックを解除する

  4. スリープ中のスレッドにシグナルまたはブロードキャストを送信する

int pthread_cond_signal(pthread_cond_t *cond);
功能:唤醒一个休眠的线程
参数:
 	@cond:条件变量的地址
返回值:成功返回0,失败返回非零

int pthread_cond_broadcast(pthread_cond_t *cond);
功能:唤醒所有休眠的线程
参数:
 	@cond:条件变量的地址
返回值:成功返回0,失败返回非零  
  1. 条件変数を破棄する
int pthread_cond_destroy(pthread_cond_t *cond);
功能:销毁条件变量
参数:
	 @cond:条件变量的地址
返回值:成功返回0,失败返回非零     
  1. スリープ中のスレッドにシグナルまたはブロードキャストを送信する
int pthread_cond_signal(pthread_cond_t *cond);
功能:唤醒一个休眠的线程
参数:
 	@cond:条件变量的地址
返回值:成功返回0,失败返回非零

int pthread_cond_broadcast(pthread_cond_t *cond);
功能:唤醒所有休眠的线程
参数:
 	@cond:条件变量的地址
返回值:成功返回0,失败返回非零  
  1. 条件変数を破棄する
int pthread_cond_destroy(pthread_cond_t *cond);
功能:销毁条件变量
参数:
	 @cond:条件变量的地址
返回值:成功返回0,失败返回非零     

おすすめ

転載: blog.csdn.net/qq_47355554/article/details/132679866