線程

線程的概念

線程:light weight process首尼,輕量級的進程价涝,Linux環(huán)境下本質(zhì)上仍是進程掀抹。和進程的區(qū)別是蹬竖,進程有獨立的地址空間和PCB型凳,而線程有PCB但是沒有獨立的地址空間丈冬。在Linux環(huán)境下,線程是最小的執(zhí)行單位甘畅,而進程是最小分配資源單位埂蕊。

線程共享資源:

  1. 文件描述符表。
  2. 每種信號的處理方式疏唾。
  3. 當(dāng)前工作目錄蓄氧。
  4. 用戶ID和組ID。
  5. 內(nèi)存地址空間(.text/.data/.bss/heap/共享庫)槐脏。

線程非共享資源:

  1. 線程id喉童。
  2. 處理器現(xiàn)場和棧指針(內(nèi)核棧)。
  3. 獨立的椂偬欤空間(用戶空間棧)堂氯。
  4. errno變量。
  5. 信號屏蔽字牌废。
  6. 進程調(diào)度優(yōu)先級咽白。

線程的優(yōu)缺點
優(yōu)點:1,提高程序并發(fā)性鸟缕。2晶框,開銷小。3懂从,數(shù)據(jù)通信授段,共享數(shù)據(jù)方便。
缺點:1莫绣,庫函數(shù)畴蒲,穩(wěn)定性稍低。2对室,調(diào)試模燥、編寫困難,gdb不支持掩宜。3蔫骂,對信號支持不好。

線程的創(chuàng)建和回收

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

struct person{
    char name[20];
    int age;
};
typedef struct person* Person;

void *func(void *arg){
    Person p = (Person)arg;
    printf("name = %s,age = %d\n",p->name,p->age);
    Person ret = (Person)malloc(sizeof(struct person));
    strcpy(ret->name,p->name);
    ret->age = p->age+1;
    return (void *)ret;
}

int main(int argc, char const *argv[])
{
    pthread_t tid;
    Person p = (Person)malloc(sizeof(struct person));
    strcpy(p->name,"Jack");
    p->age = 34;
    pthread_create(&tid,NULL,func,(void *)p);
    Person ret;
    pthread_join(tid,(void**)&ret);
    printf("name = %s,age = %d\n",ret->name,ret->age);
    free(ret);
    system("pause");
    return 0;
}

由于線程共享的東西比較多牺汤,如果不使用同步辽旋,線程之間是競爭的關(guān)系,所以,我們無法預(yù)測線程之間的執(zhí)行順序补胚,并且码耐,在線程執(zhí)行的過程中,也可能切換線程溶其。下面是一個例子:

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

int i = 0;

void *func(void *arg){
    while(1){
        i++;
        i++;
    }
}

int main(int argc, char const *argv[])
{
    pthread_t tid;
    pthread_create(&tid,NULL,func,(void *)NULL);
    while(1){
        i++;
        i++;
        if(i % 2 != 0){
            printf("error! i = %d is odd\n",i);
            break;
        }
    }
    return 0;
}

這個程序每次運行的結(jié)果都不一樣骚腥,這就是與時間有關(guān)的錯誤。產(chǎn)生這種錯誤有三個條件:

  1. 共享數(shù)據(jù)瓶逃。
  2. 多個對象競爭束铭。
  3. 沒有合理的同步機制。

要解決這種問題厢绝,需要使用同步的機制契沫。

線程使用注意事項

  1. 需要主線程退出其他線程不退出,主線程應(yīng)調(diào)用pthread_exit方法昔汉。
  2. 要避免僵尸線程懈万,使用pthread_join顯示回收,或者使用pthread_detach分離線程或者在pthread_create中指定分離屬性靶病。
  3. malloc和mmap申請的內(nèi)存可以被其他線程釋放钞速。
  4. 應(yīng)避免在多線程中調(diào)用fork,除非馬上exec嫡秕,子進程中只有調(diào)用fork的線程存在,其他線程在子進程中均pthread_exit苹威。

互斥量

Linux中提供一把互斥鎖mutex(也稱之為互斥量)昆咽。每個線程在對資源操作前都嘗試先加鎖,成功加鎖會才能操作牙甫,操作結(jié)束解鎖掷酗。資源還是共享的,線程間也還是競爭的窟哺,但通過鎖泻轰,就將資源的訪問變成互斥操作,而后與時間有關(guān)的錯誤也不會產(chǎn)生了且轨「∩互斥鎖只有一把,所以同一個時刻只有一個線程擁有這把鎖旋奢。

互斥鎖實質(zhì)上是操作系統(tǒng)提供的一把”建議鎖“(又稱”協(xié)同鎖“)泳挥,建議程序中有多線程訪問共享資源的時候使用該機制,但至朗,并沒有強制限定屉符。因此,即使有了mutex,如果有線程不按規(guī)則來訪問數(shù)據(jù)矗钟,依然會造成數(shù)據(jù)混亂唆香。如,當(dāng)A線程對某個全局變量加鎖訪問吨艇,B在訪問之前嘗試加鎖躬它,拿不到鎖,B阻塞秸应。C線程不去加鎖虑凛,而直接訪問該全局變量,依然能夠訪問软啼,但會出現(xiàn)數(shù)據(jù)混亂桑谍。

mutex有以下的一些函數(shù):

/* 獲得一個初始化好的鎖 */
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;
/* 初始化鎖 */
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
/* 加鎖,阻塞 */
int pthread_mutex_lock(pthread_mutex_t *mutex);
/* 加鎖祸挪,不阻塞 */
int pthread_mutex_trylock(pthread_mutex_t *mutex);
/* 解鎖 */
int pthread_mutex_unlock(pthread_mutex_t *mutex);
/* 釋放鎖 */
int pthread_mutex_destroy(pthread_mutex_t *mutex);

上面的程序可以改成下面的形式:

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

pthread_mutex_t mutex;
int i = 0;

void *func(void *arg){
    while(1){
        pthread_mutex_lock(&mutex);
        i++;
        i++;
        pthread_mutex_unlock(&mutex);
    }
}

int main(int argc, char const *argv[])
{
    pthread_t tid;
    pthread_mutex_init(&mutex,NULL);
    pthread_create(&tid,NULL,func,(void *)NULL);
    while(1){
        pthread_mutex_lock(&mutex);
        i++;
        i++;
        if(i % 2 != 0){
            printf("error! i = %d is odd\n",i);
            break;
        }
        pthread_mutex_unlock(&mutex);
    }
    pthread_mutex_destroy(&mutex);
    return 0;
}

在做這個demo的時候锣披,犯了一個錯誤,把unlock寫到了檢查是否為奇數(shù)之前贿条,也會出現(xiàn)上面那種現(xiàn)象雹仿,原因在于,有可能恰好在檢查前失去CPU整以,然后執(zhí)行一次i++之后又得到了CPU(此時雖然已經(jīng)上鎖了胧辽,但是不會去檢查,會繼續(xù)執(zhí)行)公黑。

死鎖及其解決方案

一個線程可以通過某種形式的加鎖機制來防止別的線程在互斥還沒有釋放的時候就訪問這個資源邑商。值得注意的是,加鎖是阻塞的凡蚜,所以可能會出現(xiàn)這種情況:某個線程在等待另一個線程人断,而后者也在等待別的線程,這樣一直下去朝蜘,直到這個鏈條上的線程又在等待第一個線程釋放鎖恶迈。這得到一個任務(wù)之間互相等待的連續(xù)循環(huán),沒有哪個線程能夠繼續(xù)谱醇,這被稱為死鎖暇仲。最簡單的情況就是自己等自己,如下面的這個程序:

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

int main(int argc, char const *argv[])
{
    pthread_mutex_t pit = PTHREAD_MUTEX_INITIALIZER;
    pthread_mutex_lock(&pit);
    printf("Lock again\n");
    pthread_mutex_lock(&pit);
    return 0;
}

再次上鎖的時候副渴,需要先釋放鎖熔吗,但是線程本身是無法釋放鎖的,所以就會進入死循環(huán)佳晶。

死鎖的產(chǎn)生需要同時滿足以下4個條件:

  1. 互斥條件桅狠。線程使用的資源中至少有一個是不能共享的。
  2. 至少有一個線程它必須持有一個資源且正在等待獲取一個當(dāng)前被別的線程持有的資源。
  3. 資源不能被搶占中跌,只能等待其他線程釋放資源咨堤。
  4. 必須有循環(huán)等待。

這些條件需要全部滿足才會產(chǎn)生死鎖漩符,所以一喘,為了避免死鎖,只需要破壞其中一個條件即可嗜暴。在程序中凸克,防止死鎖最容易的方法是破壞第4個條件。也就是說闷沥,我們要注意資源的獲取順序萎战,最好是一致的。如:A獲取順序1舆逃,2蚂维,3;B順序也是1,2,3路狮,則不會發(fā)生死鎖虫啥。而如果B的順序是3,2,1,則容易出現(xiàn)死鎖奄妨。因為后者會出現(xiàn)互相等待的情況涂籽。下面以一個哲學(xué)家吃飯的例子來說明這個問題:

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

pthread_mutex_t mutex[5];

void* philosopher(void *arg){
    int id = (int)arg;
    int left,right;
    if(id<4){
        left = id;
        right = id + 1;
    }else if(id == 4){
        /* 這里會出現(xiàn)死鎖,如果要避免死鎖砸抛,交換left和right的值即可 */
        left = id;
        right = 0;
    }
    while(1){
        /* 先拿左邊的又活,再拿右邊的 */
        pthread_mutex_lock(&mutex[left]);
        pthread_mutex_lock(&mutex[right]);
        printf("philosopher %d eating\n",id);
        pthread_mutex_unlock(&mutex[left]);
        pthread_mutex_unlock(&mutex[right]);
    }
}

int main(int argc, char const *argv[])
{
    int i;
    pthread_t th[5];
    for(i=0;i<5;i++){
        mutex[i] = PTHREAD_MUTEX_INITIALIZER;
    }
    for(i=0;i<5;i++){
        pthread_create(&th[i],NULL,philosopher,(void *)i);
    }
    for(i=0;i<5;i++){
        pthread_join(th[i],NULL);
    }
    for(i=0;i<5;i++){
        pthread_mutex_destroy(&mutex[i]);
    }
    return 0;
}

條件變量及生產(chǎn)者消費者模型

條件變量本身不是鎖!但它也可以造成線程阻塞锰悼。通常與互斥鎖配合使用,給多線程提供一個會和的場所团赏。

/* 獲得一個初始化好的鎖 */
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
/* 初始化條件變量 */
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);
/* 等待一個條件變量箕般,并且將mutex解鎖,喚醒后將mutex加鎖 */
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
/* 和wait差不多舔清,只不過到了絕對時間點會直接喚醒 */
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
/* 釋放條件變量 */
int pthread_cond_destroy(pthread_cond_t *cond);

線程同步典型的案例即為生產(chǎn)者消費者模型丝里,而借助條件變量來實現(xiàn)這一模型,是比較常見的一種方法体谒。假定有兩個線程杯聚,一個模擬生產(chǎn)者行為,一個模擬消費者行為抒痒。兩個線程操作一個共享資源幌绍,生產(chǎn)者向其中添加產(chǎn)品,消費者從中消費產(chǎn)品。具體的代碼如下:

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

int i = -1;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond_empty = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond_full = PTHREAD_COND_INITIALIZER;

void *cfunc(void *arg){
    int id = (int)arg;
    while(1){
        /* 要訪問i傀广,必須先加鎖 */
        pthread_mutex_lock(&lock);
        while(i == -1){
            /* 消費者等待滿 */
            pthread_cond_wait(&cond_full,&lock);
        }
        printf("consumer %d consume %d\n",id,i);
        i = -1;
        /* 解鎖之后通知生產(chǎn)者空了 */
        pthread_mutex_unlock(&lock);
        pthread_cond_signal(&cond_empty);
    }
}

void *pfunc(void *arg){
    int id = 0;
    while(1){
        /* 要訪問i颁独,必須加鎖 */
        pthread_mutex_lock(&lock);
        while(i != -1){
            /* 生產(chǎn)者等待空 */
            pthread_cond_wait(&cond_empty,&lock);
        }
        printf("producer procduce %d\n",++id);
        i = id;
        /* 解鎖后通知消費者滿了 */
        sleep(1);
        pthread_mutex_unlock(&lock);
        pthread_cond_signal(&cond_full);
    }
}

int main(int argc, char const *argv[])
{
    pthread_t cid1,cid2,pid;
    pthread_create(&cid1,NULL,cfunc,(void *)1);
    pthread_create(&cid2,NULL,cfunc,(void *)2);
    pthread_create(&pid,NULL,pfunc,NULL);
    
    pthread_join(cid1,NULL);
    pthread_join(cid2,NULL);
    pthread_join(pid,NULL);
    
    pthread_cond_destroy(&cond_empty);
    pthread_cond_destroy(&cond_full);
    pthread_mutex_destroy(&lock);  
    return 0;
}

相較于mutex而言,條件變量可以減少競爭伪冰。如直接使用mutex誓酒,除了生產(chǎn)者,消費者之間要競爭互斥量以外贮聂,消費者之間也需要競爭互斥量靠柑,但如果沒有產(chǎn)品,消費者之間競爭互斥鎖是無意義的吓懈。有了條件變量機制以后歼冰,只有生產(chǎn)者完成生產(chǎn),才會引起消費者之間的競爭骄瓣,提高了程序效率停巷。在我看來,阻塞相當(dāng)于while循環(huán)榕栏,而wait是sleep畔勤,喚醒之后才會繼續(xù)執(zhí)行。

在JAVA中有一個CountDownLatch類扒磁,可以用來同步一個或多個線程庆揪,強制它們等待由其他線程執(zhí)行的一組操作,這里我用C語言簡單實現(xiàn)了一個類似的場景妨托,就是5個線程同時準(zhǔn)備好了缸榛,才可以執(zhí)行后面的操作。

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

pthread_mutex_t mutex;
pthread_cond_t cond;
int n;

void *func(void *arg){
    int id = (int)arg;
    printf("thread %d preparing\n",id);
    sleep(id);
    pthread_mutex_lock(&mutex);
    n--;
    if(n==0){
        pthread_cond_broadcast(&cond);
    }else{
        pthread_cond_wait(&cond,&mutex);
    }
    pthread_mutex_unlock(&mutex);
    printf("thread %d working\n",id);
    return (void *)NULL;
}

int main(int argc, char const *argv[])
{
    mutex = PTHREAD_MUTEX_INITIALIZER;
    cond = PTHREAD_COND_INITIALIZER;
    n = 5;
    pthread_t pt[5];
    int i;
    for(i=0;i<5;i++){
        pthread_create(&pt[i],NULL,func,(void *)(i+1));
        pthread_detach(pt[i]);
    }
    pthread_exit(NULL);
    return 0;
}

信號量

信號量是進化版的互斥鎖兰伤,也就是從1變成了N内颗。由于互斥鎖的粒度較大,如果我們希望在多個線程間對某一對象的部分數(shù)據(jù)進行共享敦腔,使用互斥鎖是沒有辦法實現(xiàn)的均澳,只能將真?zhèn)€數(shù)據(jù)對象鎖住,這樣雖然達到了多線程操作共享數(shù)據(jù)正確性的目的符衔,卻無形中導(dǎo)致線程的并發(fā)性下降找前。線程從并行執(zhí)行,變成了串行執(zhí)行判族。與字節(jié)使用單線程無異躺盛。信號量,是相對折中的一種處理方式形帮,既能保證同步槽惫,數(shù)據(jù)不混亂周叮,又能提高線程并發(fā)。信號量常用的有以下一些函數(shù):

/* 初始化信號量躯枢,值為value则吟,可以設(shè)定共享還是非共享 */
int sem_init(sem_t *sem, int pshared, unsigned int value);
/* 釋放鎖 */
int sem_destroy(sem_t *sem);
/* 信號量減1,如果小于0锄蹂,就阻塞等待 */
int sem_wait(sem_t *sem);
/* 和wait類似氓仲,不阻塞 */
int sem_trywait(sem_t *sem);
/* 和wait類似,到了時間自動解除阻塞 */
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
/* 使信號量加1得糜,如果有等待sem的線程敬扛,就喚醒這些線程 */
int sem_post(sem_t *sem);

下面是一個使用信號量實現(xiàn)生產(chǎn)者消費者模型的例子:

#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>
#define MAX 100

sem_t sp,sc;
int arr[5];

void *produce(void *arg){
    int i = 0;
    int n = 1;
    while(n<=MAX){
        sem_wait(&sp);/* 拿空位置 */
        arr[i] = n++;
        i = (i+1)%5;
        sem_post(&sc);/* 產(chǎn)品數(shù)目加1 */
    }
    return (void *)NULL;
}

void *consume(void *arg){
    int i = 0;
    int n = 0;
    while(n<MAX){
        sem_wait(&sc);
        n = arr[i];
        i = (i+1)%5;
        printf("consume %d\n",n);
        sem_post(&sp);
    }
    return (void *)NULL;
}

int main(int argc, char const *argv[])
{
    sem_init(&sp,0,5);/* 開始有5個空位置 */
    sem_init(&sc,0,0);/* 開始一個產(chǎn)品也沒有 */
    pthread_t pt[2];
    pthread_create(&pt[0],NULL,produce,NULL);
    pthread_create(&pt[1],NULL,consume,NULL);
    pthread_join(pt[0],NULL);
    pthread_join(pt[1],NULL);
    sem_destroy(&sp);
    sem_destroy(&sc);
    return 0;
}

讀寫鎖

與互斥量類似,但讀寫鎖允許更高的并行性朝抖。讀寫鎖是一把鎖啥箭,只不過讀寫鎖有兩個不同的加鎖方式:可以以讀的方式加鎖,也可以以寫的方式加鎖治宣。讀寫鎖有以下的特性:

  1. 讀寫鎖是”寫模式加鎖“時急侥,解鎖前,所有對該鎖加鎖的線程都會被阻塞侮邀。
  2. 讀寫鎖是“讀模式加鎖”時坏怪,如果線程以讀模式對其加鎖會成功;如果以寫模式進行加鎖會阻塞绊茧。
  3. 讀寫鎖是“讀模式加鎖”時铝宵,既有視圖以寫模式加鎖的線程,也有試圖以讀模式加鎖的線程华畏,那么讀寫鎖會阻塞隨后的讀模式鎖鹏秋。讀鎖、寫鎖并行阻塞亡笑,寫鎖優(yōu)先級高侣夷。

總結(jié)起來就一句話:寫?yīng)氄迹x共享仑乌,寫鎖優(yōu)先級高百拓。

讀寫鎖有以下的一些常用函數(shù):

/* 初始化讀寫鎖 */
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); 
/* 銷毀鎖 */
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
/* 以讀的方式加鎖,阻塞 */
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
/* 以讀的方式加鎖,不阻塞 */
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); 
/* 以寫的方式加鎖绝骚,阻塞 */
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); 
/* 以寫的方式加鎖,不阻塞 */
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
/* 解鎖 */
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); 

下面以一個簡單的例子來說明讀寫鎖的特性:

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

pthread_rwlock_t rwlock;
int n;

void *reader(void *arg){
    int id = (int)arg;
    while(n <= 100){
        pthread_rwlock_rdlock(&rwlock);
        printf("reading thread %d reading data %d\n",id,n);
        sleep(1);
        printf("reading thread %d finish reading\n",id);   
        pthread_rwlock_unlock(&rwlock);
    }
    return NULL;
}

void *writer(void *arg){
    int id = (int)arg;
    while(n <= 100){
        pthread_rwlock_wrlock(&rwlock);
        printf("writing thread %d writing data %d\n",id,++n);
        sleep(1);
        printf("writing thread %d finish reading\n",id);   
        pthread_rwlock_unlock(&rwlock);
        sleep(1);
    }
    return NULL;
}

int main(int argc, char const *argv[])
{
    n = 0;
    pthread_rwlock_init(&rwlock,NULL);
    int i;
    pthread_t tid[3];
    for(i=0;i<2;i++){
        pthread_create(&tid[i],NULL,reader,(void *)(i+1));
    }
    pthread_create(&tid[2],NULL,writer,(void *)3);
    for(i=0;i<3;i++){
        pthread_join(tid[i],NULL);
    }
    return 0;
}

可以看到的現(xiàn)象是祠够,讀模式加鎖時压汪,其他讀線程可以進入,寫線程加鎖的時候古瓤,所有的線程都阻塞止剖。值得注意的是腺阳,如果寫線程后面不sleep一秒的話,就寫線程一個人自己玩穿香,因為解鎖之后馬上加鎖亭引,寫線程很容易搶到這把鎖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末皮获,一起剝皮案震驚了整個濱河市焙蚓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌洒宝,老刑警劉巖购公,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異雁歌,居然都是意外死亡宏浩,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門靠瞎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來比庄,“玉大人,你說我怎么就攤上這事乏盐〖岩ぃ” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵丑勤,是天一觀的道長华嘹。 經(jīng)常有香客問我,道長法竞,這世上最難降的妖魔是什么耙厚? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮岔霸,結(jié)果婚禮上薛躬,老公的妹妹穿的比我還像新娘。我一直安慰自己呆细,他們只是感情好型宝,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著絮爷,像睡著了一般趴酣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上坑夯,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天岖寞,我揣著相機與錄音,去河邊找鬼柜蜈。 笑死仗谆,一個胖子當(dāng)著我的面吹牛指巡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播隶垮,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼藻雪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了狸吞?” 一聲冷哼從身側(cè)響起勉耀,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎捷绒,沒想到半個月后瑰排,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡暖侨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年椭住,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片字逗。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡京郑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出葫掉,到底是詐尸還是另有隱情些举,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布俭厚,位于F島的核電站户魏,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏挪挤。R本人自食惡果不足惜叼丑,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望扛门。 院中可真熱鬧鸠信,春花似錦、人聲如沸论寨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽葬凳。三九已至绰垂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間火焰,已是汗流浹背劲装。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留荐健,地道東北人酱畅。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像江场,于是被迫代替她去往敵國和親纺酸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Q:為什么出現(xiàn)多線程址否? A:為了實現(xiàn)同時干多件事的需求(并發(fā))餐蔬,同時進行著下載和頁面UI刷新。對于處理器佑附,為每個線...
    幸福相依閱讀 1,570評論 0 2
  • 轉(zhuǎn)載自:https://halfrost.com/go_map_chapter_one/ https://half...
    HuJay閱讀 6,124評論 1 5
  • 線程 在linux內(nèi)核那一部分我們知道樊诺,線程其實就是一種特殊的進程,只是他們共享進程的文件和內(nèi)存等資源音同,無論如何對...
    大雄good閱讀 662評論 0 2
  • 線程 線程的概念 典型的UNIX進程可以看成只有一個控制線程:一個進程在同一時刻只做一件事词爬。有了多個控制線程后,在...
    ColdWave閱讀 1,451評論 0 0
  • 多線程系列文章源碼頭文件內(nèi)容: #include #include #include 作為程序員权均,就是要減少重復(fù)勞...
    batbattle閱讀 913評論 0 1