Linux 多線程 - 線程異步與同步機制

Linux 多線程 - 線程異步與同步機制

I. 同步機制

線程間的同步機制主要包括三個:

  • 互斥鎖
    以排他的方式,防止共享資源被并發訪問;
    互斥鎖為二元變量, 狀態為0-開鎖、1-上鎖;
    開鎖必須由上鎖的線程執行,不受其它線程干擾.

  • 條件變量
    滿足某個特定條件時,可通過條件變量通知其它線程do-something;
    必須與互斥鎖*聯合使用,單獨無法執行.

  • 讀寫鎖
    針對多讀者,少寫者的情況設定

> *   允許**多讀**,但此時**不可寫**;
>     
> *   **唯一寫**,此時**不可讀**.

函數的頭文件為:

    #include <phtread.h>     

1. 互斥鎖

操作流程:

  • I. 創建互斥鎖

  • II. 申請鎖:若可用,立刻占用;否則,阻塞等待

  • III. do-something

  • IV. 釋放鎖

  • V. 銷毀鎖

以下是互斥鎖的基本操作函數:

功能 函數 參數 返回值 說明
初始化鎖 int pthread_mutex_init(

pthread_mutex_t *mutex,
const pthread_mutexattr_t *attr) | 1. mutex: 欲建立的互斥鎖
2.attr:屬性,一般為NULL | 成功:0
失?。悍橇阒?| |
| 阻塞申請鎖 | int pthread_mutex_lock(
pthread_mutex_t *mutex) | mutex:互斥鎖 | 成功:0
失?。悍橇阒?| 若未申請到,
阻塞等待 |
| 非阻塞申請 | int pthread_mutex_trylock(
pthread_mutex_t *mutex) | mutex:互斥鎖 | 成功:0
失?。悍橇阒?| 若未申請到,
返回錯誤 |
| 釋放鎖 | int pthread_mutex_unlock(
pthread_mutex_t *mutex) | mutex:互斥鎖 | 成功:0
失?。悍橇阒?| |
| 銷毀鎖 | int pthread_mutex_destroy(
pthread_mutex_t *mutex) | mutex:互斥鎖 | 成功:0
失?。悍橇阒?| |

2. 條件變量

注意,條件變量必須與互斥鎖共同使用;

以下是條件變量的基本操作函數:

功能 函數 參數 返回值 說明
初始化鎖 int pthread_cond_init(

pthread_cond_t *cond,
const pthread_condattr_t *attr) | 1. cond: 欲建立的條件變量
2.attr:屬性,一般為NULL | 成功:0
失?。悍橇阒?| |
| 等待條件變量 | int pthread_cond_wait(
pthread_cond_t *cond,
pthread_mutex_t *mutex) | 1.cond:條件變量
2.mutex:互斥鎖 | 成功:0
失?。悍橇阒?| 阻塞等待
隱含釋放申請到的互斥鎖 |
| 限時等待條件變量 | int pthread_cond_timewait(
pthread_cond_t *cond,
pthread_mutex_t *mutex,
const struct timespec *time) | 3.time:等待過期的絕對時間
從1970-1-1:0:0:0起 | 成功:0
失敗:非零值 | struct timespec{long ts_sec;
long ts_nsec} |
| 單一通知 | int pthread_cond_signal(
pthread_cond_t *cond) | cond:條件變量 | 成功:0
失?。悍橇阒?| 喚醒等待cond的第一個線程
隱含獲取需要的互斥鎖 |
| 廣播通知 | int pthread_cond_broadcast(
pthread_cond_t *cond) | cond:條件變量 | 成功:0
失?。悍橇阒?| 喚醒所有等待cond的線程
隱含獲取需要的互斥鎖 |
| 銷毀條件變量 | int pthread_cond_destroy(
pthread_cond_t *cond) | cond:條件變量 | 成功:0
失?。悍橇阒?| |

3. 讀寫鎖

讀寫基本原則:

  • 若當前線程讀數據,則允許其他線程讀數據,但不允許寫

  • 若當前線程寫數據,則不允許其他線程讀、寫數據

以下是基本的操作:

功能 函數 參數 返回值 說明
初始化鎖 int pthread_rwlock_init(

pthread_rwlock_t *rwlock,
const pthread_rwlockattr_t *attr) | 1. rwlock: 欲建立的讀寫鎖
2.attr:屬性,一般為NULL | 成功:0
失敗:非零值 | |
| 阻塞申請讀鎖 | int pthread_rwlock_rdlock(
pthread_rwlock_t *rwlock) | rwlock:讀寫鎖 | 成功:0
失敗:非零值 | 若未申請到,
阻塞等待 |
| 非阻塞申請 | int pthread_rwlock_tryrdlock(
pthread_rwlock_t *rwlock) | rwlock:讀寫鎖 | 成功:0
失敗:非零值 | 若未申請到,
返回錯誤 |
| 阻塞申請寫鎖 | int pthread_rwlock_wrlock(
pthread_rwlock_t *rwlock) | rwlock:讀寫鎖 | 成功:0
失?。悍橇阒?| 若未申請到,
阻塞等待 |
| 非阻塞申請寫鎖 | int pthread_rwlock_trywrlock(
pthread_rwlock_t *rwlock) | rwlock:讀寫鎖 | 成功:0
失敗:非零值 | 若未申請到,
返回錯誤 |
| 釋放鎖 | int pthread_mutex_unlock(
pthread_rwlock_t *rwlock) | rwlock:讀寫鎖 | 成功:0
失?。悍橇阒?| |
| 銷毀鎖 | int pthread_rwlock_destroy(
pthread_rwlock_t *rwlock) | rwlock:讀寫鎖 | 成功:0
失敗:非零值 | |

4. 線程信號量

線程信號量類似進程的信號量,主要是使得多個線程訪問共享資源時,順序互斥訪問。
與互斥鎖的區別在于:

  • 互斥鎖:只有一個bool類型的值,只允許2個線程進行排隊;
  • 信號量:允許多個線程共同等待一個共享資源

函數如下:

     #include <semaphore.h>
功能 函數 參數 返回值 說明
創建信號量 int sem_init(sem_t *sem,
int pshared, unsigned int value) 1. sem:信號量地址;

2. pshared:是(!=0)否(0)為共享信號量
3. value:信號量初值 | 0: 成功
-1: 失敗 | |
| P操作(阻塞) | int sem_wait(sem_t *sem) | sem:信號量地址 | 0: 成功
-1: 失敗 | |
| P操作(非阻塞) | int sem_trywait(sem_t *sem) | sem:信號量地址 | 0: 成功
-1: 失敗 | |
| P操作(時間) | int sem_timedwait(sem_t *sem,
const struct timespec *abs_timeout) | 1. sem:信號量地址
2. abs_timeout:超時時間 | 0: 成功
-1: 失敗 | struct timespec 見下面 |
| V操作 | int sem_post(sem_t *sem) | sem:信號量地址 | 0: 成功
-1: 失敗 | |
| 獲取信號量值 | int sem_getvalue(sem_t *sem, int *sval) | 1. sem:信號量地址
2. sval: 將信號量值放到該地址 | 0: 成功
-1: 失敗 | |
| 刪除信號量 | int sem_destroy(sem_t *sem) | sem:信號量地址 | 0: 成功
-1: 失敗 | |

struct timespec {
               time_t tv_sec;      /* Seconds */
               long   tv_nsec;     /* Nanoseconds [0 .. 999999999] */
           };

II. 異步機制 - 信號

線程的異步機制只有信號,類似于線程的信號。

線程信號具備以下特點

    1. 任何線程都可以向其它線程(同一進程下)發送信號;
    1. 每個線程都具備自己獨立的信號屏蔽集,不影響其它線程;
    1. 線程創建時,不繼承原線程的信號屏蔽集;
    1. 同進程下,所有線程共享對某信號的處理方式,即一個設置,所有有效;
    1. 多個線程的程序,向某一個線程發送終止信號,則整個進程終止

信號的基本操作如下:

功能 函數 參數 返回值 說明
安裝信號 sighandler_t signal(

int signum,
sighandler_t handler) | 1.signum:信號值
2.handler:信號操作 | ? | 詳情參見:
http://www.cnblogs.com/Jimmy1988/p/7575103.html |
| 發送信號 | int pthread_kill(
pthread_t threadid,
int signo | 1.threadid: 目標線程id
2.signo:信號值 | 成功:0
失?。悍橇阒?| 若signo=0,
檢測該線程是否存在,
不發送信號 |
| 設置屏蔽集 | pthread_sigmask(int how,
const sigset_t *set,
sigset_t *oldset) | 1.how:如何更改信號掩碼
2.newmask:新的信號屏蔽集
3.原信號屏蔽集 | 成功:0
失?。悍橇阒?| how值:
?1.SIG_BLOCK:添加新掩碼
?2.SIG_UNBLOCK:刪除新掩碼
?3.SIG_SETMASK:設置新掩碼完全替換舊值 |

也可以參考這篇博客:https://www.cnblogs.com/coding-my-life/p/4782529.html

III、示例代碼

1.同步機制:

1). 互斥鎖:

兩個線程:

  • 讀線程:從stdin中讀取數據,并存儲
  • 寫線程:從存儲buffer中讀取數據并顯示
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>

#define SIZE 128

pthread_mutex_t mutex;
int EXIT = 0;
char word[SIZE];

void * child(void *arg)
{
    while(1)
    {
        while(strlen(word) == 0)
            usleep(100);

        pthread_mutex_lock(&mutex);
        printf("The input words: %s\n", word);
        pthread_mutex_unlock(&mutex);
        if(strcmp("end\n", word) == 0)
        {
            printf("The process end\n");
            EXIT = 1;
            break;
        }

        memset(word, '\0', SIZE);
    }

    return ;
}

int main()
{
    //1\. create the lock
    pthread_mutex_init(&mutex, NULL);

    //2.create a new thread
    pthread_t tid;
    pthread_create(&tid, NULL, (void *)*child, NULL);

    //3\. Input words
    while(EXIT == 0)
    {
        if(strlen(word)!=0)
            usleep(100);
        //add the lock
        else
        {
            pthread_mutex_lock(&mutex);
            printf("Input words:  ");
            fgets(word, SIZE, stdin);
            pthread_mutex_unlock(&mutex);            
        }
    }

    pthread_join(tid, NULL);
    printf("The child has joined\n");
    pthread_mutex_destroy(&mutex);

    return 0;
}

2). 條件變量:

生產者和消費者問題:

  • 生產者:
    向倉庫生產數據(大小可任意設定),當滿時,阻塞等待倉庫有空閑(由消費者消費完后通知)

  • 消費者:
    從倉庫讀數據,若倉庫為空,則阻塞等待,當生產者再次生產產品后通知

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

#define SIZE 2
int Data[SIZE];

typedef struct
{
    pthread_mutex_t lock;
    pthread_cond_t  notFull;
    pthread_cond_t  notEmpty;
    int read_point;
    int write_point;
}sCOND;

sCOND *pCondLock;

void init(void)
{
    //memset(pCondLock, 0, sizeof(sCOND));

    //1.Create a mutex lock
    pthread_mutex_init(&pCondLock->lock, NULL);

    //2.Create two condition variable  
    pthread_cond_init(&pCondLock->notFull, NULL);
    pthread_cond_init(&pCondLock->notEmpty, NULL);

    //set the read and write point 0
    pCondLock->read_point = 0;
    pCondLock->write_point = 0;
}

int put(int data) 
{
    //obtain the mutex lock
    pthread_mutex_lock(&pCondLock->lock);

    //check the global variable Data full or not
    while((pCondLock->write_point+1)%SIZE == pCondLock->read_point)
    {
        printf("The buf is full, waitting for not_full signal\n");
        pthread_cond_wait(&pCondLock->notFull, &pCondLock->lock);
    }       

    //write the data to buffer
    Data[pCondLock->write_point] = data;
    pCondLock->write_point++;
    if(pCondLock->write_point == SIZE)
        pCondLock->write_point = 0;

    //unlock the mutex lock 
    pthread_mutex_unlock(&pCondLock->lock);

    //wake up the not_empty signal
    pthread_cond_signal(&pCondLock->notEmpty);

    return 0;
}

int get(int *data) 
{
    //obtain the mutex lock
    pthread_mutex_lock(&pCondLock->lock);

    //check the global variable Data empty or not
    while(pCondLock->write_point == pCondLock->read_point)
    {
        printf("The buf is empty, waitting for not_empty signal\n");
        pthread_cond_wait(&pCondLock->notEmpty, &pCondLock->lock);
    }       

    //read the data from buffer
    *data = Data[pCondLock->read_point];
    pCondLock->read_point++;
    if(pCondLock->read_point == SIZE)
        pCondLock->read_point = 0;

    //wake up the not_empty signal
    pthread_cond_signal(&pCondLock->notFull);

    pthread_mutex_unlock(&pCondLock->lock);

    return *data;
}

void *produce(void)
{
    int times=0;
    //1\. first 5 times, every second write a data to buffer
    for(times=0; times < 5; times++)
    {
        sleep(1);
        put(times+1);
        printf("Input date=%d\n", times+1);
    }

    //2\. last 5 times, every 3 seconds write a data to buffer 
    for(times = 5; times < 10; times++)
    {
        sleep(3);
        put(times+1);
        printf("Input date=%d\n", times+1);
    }
}

void *consume(void)
{
    int times=0;
    int data=0;
    //10 times, every 2 seconds read the buffer
    for(times = 0; times < 10; times++)
    {
        sleep(2);
        data = get(&data);
        printf("The data is %d\n", data);
    }
}

int main()
{
    pthread_t tid1, tid2;

    pCondLock = malloc(sizeof(sCOND));
    memset(pCondLock, '\0', sizeof(sCOND));
    //1.init the struct of sCondLock 
    init();

    //2\. start two threads
    pthread_create(&tid1, NULL, (void*)*produce, NULL);
    pthread_create(&tid2, NULL, (void*)*consume, NULL);

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    free(pCondLock);

    return 0;
}

3). 讀寫鎖:

四個線程:兩讀兩寫;

多進程可同時讀,但此時不可寫;
只有一個線程可寫,其它線程等待該線程寫完后執行響應的讀/寫操作

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

#define BUF_SIZE 128

char buf[BUF_SIZE];

pthread_rwlock_t rwlock;

int time_to_exit = 0;

void *read_first(void *arg);
void *read_second(void *arg);
void *write_first(void *arg);
void *write_second(void *arg);

int main()
{
    pthread_t tid_rd1, tid_rd2;
    pthread_t tid_wr1, tid_wr2;

    //1.create a read-write-lock
    int ret = pthread_rwlock_init(&rwlock, NULL);
    if(ret != 0)
    {
        perror("pthread_rwlock_init");
        exit(EXIT_FAILURE);
    }

    //2\. Create the read and write threads
    ret = pthread_create(&tid_rd1, NULL, (void *)*read_first, NULL);
    if(ret != 0)
    {
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }

    ret = pthread_create(&tid_rd2, NULL, (void *)*read_second, NULL);
    if(ret != 0)
    {
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }

    ret = pthread_create(&tid_wr1, NULL, (void *)*write_first, NULL);
    if(ret != 0)
    {
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }

    ret = pthread_create(&tid_wr2, NULL, (void *)*write_second, NULL);
    if(ret != 0)
    {
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }

    //3\. wait for the threads finish
    pthread_join(tid_rd1, NULL);
    pthread_join(tid_rd2, NULL);
    pthread_join(tid_wr1, NULL);
    pthread_join(tid_wr2, NULL);

    //4\. delete the read-write-lock
    pthread_rwlock_destroy(&rwlock);

    return 0;
}

/***************************************************/
// Write threads
void *write_first(void *arg)
{
    while(!time_to_exit)
    {
        sleep(5);

        //1\. get the read-lock    
        pthread_rwlock_wrlock(&rwlock);
        printf("\nThis is thread write_first!\n");

        printf("Pls input the string: ");
        fgets(buf, BUF_SIZE, stdin);

        pthread_rwlock_unlock(&rwlock);
    }

    printf("Exit the write_first!\n");
    pthread_exit(0);
}

void *write_second(void *arg)
{
    while(!time_to_exit)
    {
        sleep(10);

        //1\. get the read-lock    
        pthread_rwlock_wrlock(&rwlock);
        printf("\nThis is thread write_second!\n");

        printf("Pls input the string: ");
        fgets(buf, BUF_SIZE, stdin);

        pthread_rwlock_unlock(&rwlock);
    }

    printf("Exit the write_second!\n");
    pthread_exit(0);
}

//-----2\. read the threads
void *read_first(void *arg)
{
    while(1)
    {
        sleep(5);
        pthread_rwlock_rdlock(&rwlock);
        printf("\nThis is thread read_first\n");

        //if write an string of "end"
        if(!strncmp("end", buf, 3))
        {
            printf("Exit the read_first!\n");
            break;
        }

        //if nothing in the BUFFER
        while(strlen(buf) == 0)
        {
            pthread_rwlock_unlock(&rwlock);
            sleep(2);
            pthread_rwlock_rdlock(&rwlock);
        }

        //output the string in BUFFER
        printf("The string is: %s\n", buf);

        pthread_rwlock_unlock(&rwlock);
    }

    pthread_rwlock_unlock(&rwlock);

    //make the exit true
    time_to_exit = 1;

    pthread_exit(0);
}

void *read_second(void *arg)
{
    while(1)
    {
        sleep(4);

        pthread_rwlock_rdlock(&rwlock);
        printf("\nThis is thread read_second\n");

        //if write an string of "end"
        if(!strncmp("end", buf, 3))
        {
            printf("Exit the read_second!\n");
            break;
        }

        //if nothing in the BUFFER
        while(strlen(buf) == 0)
        {
            pthread_rwlock_unlock(&rwlock);
            sleep(2);
            pthread_rwlock_rdlock(&rwlock);
        }

        //output the string in BUFFER
        printf("The string is: %s\n", buf);

        pthread_rwlock_unlock(&rwlock);
    }

    pthread_rwlock_unlock(&rwlock);

    //make the exit true
    time_to_exit = 1;

    pthread_exit(0);
}

2. 異步機制 - 信號:

本程序包括兩個線程:

  • 線程1安裝SIGUSR1,阻塞除SIGUSR2外的所有信號;

  • 線程2安裝SIGUSR2,不阻塞任何信號

操作流程:

  • 1- 線程1、2安裝信號;

  • 2- 主線程發送SIGUSR1和SIGUSR2至線程1和線程2;

  • 3- 線程1接收到除SIGUSR2之外的信號,阻塞不執行;當收到SIGUSR2后,執行對應操作;

  • 4- 線程2接收到SIGUSR1和SIGUSR2后,分別執行對應操作

  • 5- 主線程發送SIGKILL信號,結束整個進程

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

void *th_first(void *arg);

void *th_second(void *arg);

pthread_t tid1, tid2;

void handler(int signo)
{
    printf("In handler: tid_%s, signo=%d\n", ((pthread_self() == tid1)?"first":"second"), signo);
}

int main()
{
    int ret = 0;
    //1. create first thread
    ret = pthread_create(&tid1, NULL, (void *)*th_first, NULL);
    if(0 !=ret)
    {
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }
    //2. create second thread
    ret = pthread_create(&tid2, NULL, (void *)*th_second, NULL);
    if(0 !=ret)
    {
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }

    sleep(2);
    //3. send the signal of SIG_USER1 and SIG_USER2 to thread_first
    ret = pthread_kill(tid1, SIGUSR1);
    if(0 !=ret)
    {
        perror("pthread_kill");
        exit(EXIT_FAILURE);
    }

    ret = pthread_kill(tid1, SIGUSR2);
    if(0 !=ret)
    {
        perror("pthread_kill");
        exit(EXIT_FAILURE);
    }

    //4. send the signal of SIG_USER1 and SIG_USER2 to thread_second_
    sleep(1);
    ret = pthread_kill(tid2, SIGUSR1);
    if(0 !=ret)
    {
        perror("pthread_kill");
        exit(EXIT_FAILURE);
    }

    ret = pthread_kill(tid2, SIGUSR2);
    if(0 !=ret)
    {
        perror("pthread_kill");
        exit(EXIT_FAILURE);
    }

    sleep(1);
    //5. send SIGKILL to all threads
    ret = pthread_kill(tid1, SIGKILL);
    if(0 !=ret)
    {
        perror("pthread_kill");
        exit(EXIT_FAILURE);
    }

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    return 0;
}

void *th_first(void *arg)
{
    //1. Add SIGUSR1 signal
    signal(SIGUSR1, handler);

    //2. Set the sinagl set
    sigset_t set;
    sigfillset(&set);                           //init set to be full, include all signal
    sigdelset(&set, SIGUSR2);                   //delete the SIGUSR2 from the set variable
    pthread_sigmask(SIG_SETMASK, &set, NULL);   //set the current mask set to be defined set variable

    //3. Circular wait the signal
    int i;
    for(i=0; i<5; i++)
    {
        printf("\nThis is th_first, tid=%#x\n ", pthread_self());
        pause();
    }
}

void *th_second(void *arg)
{
    usleep(100);
    //1. Add the signal of SIGUSR2
    signal(SIGUSR2, handler);

    //2. Circular wait the signal
    int i;
    for(i=0; i<5; i++)
    {
        printf("\nThis is th_second, tid=%#x\n", pthread_self());
        pause();
    }
}
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容

  • 多線程系列文章源碼頭文件內容: #include #include #include 作為程序員,就是要減少重復勞...
    batbattle閱讀 953評論 0 1
  • Q:為什么出現多線程? A:為了實現同時干多件事的需求(并發),同時進行著下載和頁面UI刷新。對于處理器,為每個線...
    幸福相依閱讀 1,607評論 0 2
  • 線程基礎 線程是進程的一個執行單元,執行一段程序片段,線程共享全局變量;線程的查看可以使用命令或者文件來進行查看;...
    秋風弄影閱讀 749評論 0 0
  • 轉自:Youtherhttps://www.cnblogs.com/youtherhome/archive/201...
    njukay閱讀 1,628評論 0 52
  • 摘要 線程概念,線程與進程的區別與聯系學會線程控制,線程創建,線程終止,線程等待了解線程分離與線程安全學會線程同步...
    狼之足跡閱讀 475評論 2 3