線程同步-條件變量解析

概念

線程同步的方法有多種惶看,互斥量、信號(hào)量六孵、條件變量纬黎、讀寫鎖等〗僦希互斥量在允許或阻塞對(duì)臨界區(qū)的訪問上是很有效的本今,線程是在對(duì)已加鎖的互斥量加鎖時(shí)發(fā)生阻塞;條件變量則允許線程由于一些未達(dá)到的條件而阻塞主巍,此處的“條件”可以由用戶來定義冠息,在訪問該條件時(shí)需要加鎖(互斥量),如果條件沒達(dá)到孕索,線程將阻塞在該條件上逛艰。
條件變量特別適用于多個(gè)線程等待某個(gè)條件的發(fā)生。如果不使用條件變量搞旭,那么每個(gè)線程就需要不斷嘗試獲得互斥鎖并檢查條件是否發(fā)生散怖,這樣大大浪費(fèi)了系統(tǒng)的資源菇绵。

條件變量與互斥量通常一起使用,原因是線程在因條件未滿足而阻塞并等待前镇眷,需要訪問“條件”咬最,而“條件”是允許其它線程修改的,因此欠动,訪問“條件”時(shí)需要加鎖永乌,訪問結(jié)束后釋放鎖。所以具伍,這也就可以解釋翅雏,條件變量的等待操作pthread_cond_wait()需要一個(gè)互斥量參數(shù),在pthread_cond_wait()內(nèi)部將調(diào)用線程放到等待隊(duì)列上后沿猜,要解鎖互斥量枚荣,以讓其它線程可以訪問“條件”;否則啼肩,該線程將一直占用互斥量橄妆,其它線程將不能訪問“條件”。不過祈坠,pthread_cond_wait()內(nèi)的解鎖互斥量只是臨時(shí)的害碾,在其它線程修改“條件”使其滿足要求并喚醒當(dāng)前阻塞線程時(shí),pthread_cond_wait()內(nèi)部又會(huì)鎖住互斥量赦拘,這樣做的目的是:

  1. 使互斥量的狀態(tài)在進(jìn)入慌随、退出pthread_cond_wait()函數(shù)時(shí)保持一致;
  2. 本身“判斷‘條件’是否滿足要求躺同,否則阻塞并等待‘條件’”這部分程序?qū)儆谂R界區(qū)阁猜,在訪問臨界區(qū)前對(duì)互斥量加鎖,退出臨界區(qū)后對(duì)互斥量解鎖是應(yīng)有的操作蹋艺,也就是說剃袍,真正地釋放互斥量鎖的操作在退出臨界區(qū)后;所以捎谨,在pthread_cond_wait()函數(shù)結(jié)束時(shí)民效,應(yīng)保持互斥量的加鎖狀態(tài);

通常涛救,“判斷‘條件’是否滿足要求畏邢,否則阻塞并等待‘條件’”這部分臨界區(qū)代碼中的“判斷‘條件’”采用while循環(huán)來做,因?yàn)閜thread_cond_wait()在阻塞并等待的過程中检吆,“條件”滿足要求后舒萎,其它線程會(huì)調(diào)用pthread_cond_signal()或pthread_cond_broadcast()喚醒阻塞線程,在pthread_cond_wait()將其從阻塞隊(duì)列放到就緒隊(duì)列及pthread_cond_wait()重新獲取互斥鎖之間蹭沛,其它線程有可能又改變了“條件”逆甜,使得此時(shí)的“條件”已不再滿足要求虱肄;因此,若不采用while()循環(huán)判斷“條件”是否成立交煞,在阻塞線程被喚醒之后咏窿,它以為“條件”滿足要求,實(shí)際上已經(jīng)被其它線程修改了素征。

當(dāng)其它線程修改了“條件”使之滿足要求后集嵌,會(huì)調(diào)用pthread_cond_signal()或pthread_cond_broadcast()發(fā)送信號(hào),發(fā)送信號(hào)的步驟順序有兩種:

  • 順序一
    1. 調(diào)用pthread_mutex_lock()對(duì)互斥量加鎖御毅;
    2. 改變條件使之滿足要求根欧;
    3. 向阻塞并等待條件的線程發(fā)送信號(hào)(比如調(diào)用pthread_cond_broadcast());
    4. 調(diào)用pthread_mutex_unlock()對(duì)互斥量解鎖端蛆;
  • 順序二
    1. 調(diào)用pthread_mutex_lock()對(duì)互斥量加鎖凤粗;
    2. 改變條件使之滿足要求;
    3. 調(diào)用pthread_mutex_unlock()對(duì)互斥量解鎖今豆;
    4. 向阻塞并等待條件的線程發(fā)送信號(hào)(比如調(diào)用pthread_cond_broadcast())嫌拣;

這兩種步驟順序都可以,但都存在一些不足呆躲。在順序一中异逐,發(fā)送條件成立信號(hào)的步驟在對(duì)互斥量解鎖之前,也就是發(fā)送線程仍是占有鎖的插掂,當(dāng)阻塞線程收到信號(hào)后結(jié)束休眠灰瞻,但pthread_cond_wait()在退出之前會(huì)對(duì)互斥量重新加鎖,可發(fā)送信號(hào)的線程尚未釋放鎖辅甥,所以剛結(jié)束休眠的阻塞線程酝润,對(duì)互斥量加鎖又導(dǎo)致阻塞了;在順序二中璃弄,發(fā)送條件成立信號(hào)的步驟在對(duì)互斥量解鎖之后要销,此時(shí)發(fā)送信號(hào)時(shí),發(fā)送線程已經(jīng)解鎖互斥量谢揪,但在剛解鎖互斥量之后蕉陋,有可能其它線程在發(fā)送線程發(fā)送信號(hào)之前捐凭,成功對(duì)互斥量加鎖拨扶,拿到了“條件”的訪問權(quán),因此茁肠,可以修改“條件”患民,這樣一來,使得“條件”不再滿足阻塞線程的要求垦梆,但發(fā)送線程不知道匹颤,仍會(huì)調(diào)用pthread_cond_broadcast()發(fā)送信號(hào)仅孩,阻塞線程收到信號(hào)后被喚醒,可此時(shí)的“條件”是不滿足要求的印蓖,這一點(diǎn)可以通過while循環(huán)判斷“條件”是否成立來修正辽慕,即便阻塞線程被喚醒,但它仍會(huì)判斷“條件”是否成立赦肃,不成立則繼續(xù)阻塞等待溅蛉。

實(shí)例 - 讀寫鎖的實(shí)現(xiàn)

目標(biāo)

通過線程通信原語實(shí)現(xiàn)讀寫鎖,讀寫鎖的要求:

  1. 同一時(shí)間他宛,允許多個(gè)讀者讀取數(shù)據(jù)船侧;
  2. 同一時(shí)間,只有一個(gè)寫者寫數(shù)據(jù)厅各;
  3. 寫優(yōu)先級(jí)比讀優(yōu)先級(jí)高镜撩。

分析

最簡(jiǎn)單的讀寫鎖實(shí)現(xiàn)

最簡(jiǎn)單的讀寫鎖實(shí)現(xiàn)如下,只用一個(gè)互斥量表示讀寫鎖队塘,任何讀操作或?qū)懖僮鞫急仨毾葘?duì)互斥量加鎖袁梗,在讀、寫完畢之后人灼,再釋放鎖围段。這樣實(shí)現(xiàn)的讀寫鎖雖然能保證讀和寫是互斥的,但有如下弊端:

  1. 讀操作之間也是互相排斥的投放,由于多個(gè)讀操作并不會(huì)改變共享區(qū)的內(nèi)容奈泪,所以這樣加鎖再釋放鎖的讀寫鎖實(shí)現(xiàn),大大降低了讀取的效率灸芳;
  2. 無法保證寫操作的優(yōu)先級(jí)高于讀操作涝桅,下面的這種實(shí)現(xiàn)方式將讀、寫放到了同一層級(jí)上烙样,訪問共享區(qū)的操作權(quán)取決于誰先對(duì)互斥量成功加鎖冯遂。
pthread_mutex_t rw_lock;
int rw_lock_init(pthread_mutex_t* prw_lock)
{
  pthread_mutex_init(prw_lock, NULL);
  return 0;
}

int rw_lock_lock(pthread_mutex_t* prw_lock)
{
  pthread_mutex_lock(prw_lock);
  return 0;
}

int rw_lock_unlock(pthread_mutex_t* prw_lock)
{
  pthread_mutex_unlock(prw_lock);
  return 0;
}

優(yōu)化方法

互斥鎖的目的,是為了避免出現(xiàn)如下競(jìng)爭(zhēng)條件:一個(gè)線程在未完成讀操作之前谒获,另一個(gè)線程寫操作改變了數(shù)據(jù)蛤肌,或者多個(gè)線程同時(shí)進(jìn)行寫操作;而在多個(gè)讀操作之間是沒有必要互斥的批狱。解決方式是引入對(duì)“讀者數(shù)量”的計(jì)數(shù)裸准。

讀寫鎖優(yōu)化一 - 讀操作之間共享

第一次優(yōu)化之后的讀寫鎖實(shí)現(xiàn)如下,引入了對(duì)“讀者數(shù)量”的計(jì)數(shù)赔硫,由于“讀者數(shù)量”是動(dòng)態(tài)變化的炒俱,由讀者間共享,屬共享變量,所以加入reader_mutex互斥量對(duì)其保護(hù)权悟,以保證讀者對(duì)其訪問的互斥砸王;寫者之間、讀者與寫者之間通過write_mutex達(dá)到互斥峦阁。當(dāng)讀者調(diào)用讀鎖加鎖操作時(shí)谦铃,首先判斷調(diào)用者是否是第一個(gè)讀者,若是榔昔,則對(duì)寫鎖加鎖荷辕;若不是第一個(gè)讀者,即前面仍有多個(gè)讀者件豌,則在釋放reader_mutex后直接訪問共享數(shù)據(jù)疮方。這樣做的結(jié)果是,在第一個(gè)讀者讀取共享數(shù)據(jù)前茧彤,對(duì)寫鎖加鎖骡显,導(dǎo)致讀寫之間互斥,之后的讀者再讀取共享數(shù)據(jù)時(shí)曾掂,便不用再因等待寫鎖而阻塞(“最簡(jiǎn)單的讀寫鎖”方法)惫谤,即讀者之間是不互斥的,可以有多個(gè)讀者訪問共享數(shù)據(jù)珠洗。
但這樣的讀寫鎖實(shí)現(xiàn)溜歪,仍有一個(gè)弊端:無法保證寫操作的優(yōu)先級(jí)高于讀操作,當(dāng)有大量讀者訪問共享數(shù)據(jù)時(shí)许蓖,寫者想對(duì)共享數(shù)據(jù)寫入蝴猪,必須等到所有讀者退出之后,才得以操作膊爪,出現(xiàn)“寫者饑餓”的情況自阱。

typedef struct rw_lock_t
{
  pthread_mutex_t reader_mutex;
  pthread_mutex_t write_mutex;
  int reader_counts;
} rw_lock;

int rw_lock_init(rw_lock* prw_lock)
{
  pthread_mutex_init(&prw_lock->reader_mutex, NULL);
  pthread_mutex_init(&prw_lock->write_mutex, NULL);
  reader_counts = 0;
}

int rw_lock_read_lock(rw_lock* prw_lock)
{
  pthread_mutex_lock(&prw_lock->reader_mutex);
  if(0 == reader_counts++)
  {
    pthread_mutex_lock(&prw_lock->write_mutex);
  }
  pthread_mutex_unlock(&prw_lock->reader_mutex);
  return 0;
}

int rw_lock_read_unlock(rw_lock* prw_lock)
{
  pthread_mutex_lock(&prw_lock->reader_mutex);
  if(0 == --reader_counts)
  {
    pthread_mutex_unlock(&prw_lock->write_mutex);
  }
  pthread_mutex_unlock(&prw_lock->reader_mutex);
  return 0;
}

int rw_lock_write_lock(rw_lock* prw_lock)
{
  pthread_mutex_lock(&prw_lock->write_lock);
  return 0;
}

int rw_lock_write_unlock(rw_lock* prw_lock)
{
  pthread_mutex_unlock(&prw_lock->write_lock);
  return 0;
}

讀寫鎖優(yōu)化二 - 寫操作優(yōu)先級(jí)高于讀操作

深入分析

讀寫鎖要滿足的條件
  1. 寫者的優(yōu)先級(jí)大于讀者
    這條規(guī)則主要體現(xiàn)讀者進(jìn)入共享區(qū)和寫者離開共享區(qū)的判斷流程。
    (1) 當(dāng)讀者進(jìn)入共享區(qū)時(shí)米酬,應(yīng)首先檢查是否有寫者阻塞沛豌,再檢查是否有寫者在寫,再檢查是否有讀者已在共享區(qū)赃额;只有沒有寫者阻塞或?qū)懻咴趯憰r(shí)加派,讀者才能進(jìn)入共享區(qū);(2)當(dāng)寫者離開共享區(qū)時(shí)跳芳,應(yīng)首先檢查是否有寫者在阻塞芍锦,再檢查是否有讀者阻塞;只有沒有寫者阻塞時(shí)筛严,才能繼續(xù)對(duì)讀者是否阻塞做判斷醉旦;
  2. 讀、寫之間互斥
    這條規(guī)則的含義是只要一方在共享區(qū)內(nèi)桨啃,另一方就必須阻塞车胡,不得進(jìn)入,只有等到在共享區(qū)內(nèi)的一方退出時(shí)照瘾,另一方才有機(jī)會(huì)進(jìn)入匈棘。
    (1) 當(dāng)讀者離開共享區(qū)時(shí),應(yīng)首先檢查共享區(qū)內(nèi)的讀者是否已全部退出析命,若是主卫,再檢查是否有寫者在阻塞,若有鹃愤,則喚醒寫者(寫者阻塞有兩種原因?qū)е拢?. 有讀者正在共享區(qū)內(nèi)讀取數(shù)據(jù)簇搅,寫者需等到當(dāng)前讀者都退出后,再進(jìn)入共享區(qū)软吐;2. 當(dāng)前共享區(qū)內(nèi)正有寫者在寫瘩将,此時(shí)想進(jìn)入共享區(qū)的寫者需阻塞)(2) 當(dāng)寫者進(jìn)入共享區(qū)時(shí)凹耙,首先檢查當(dāng)前共享區(qū)內(nèi)是否有讀者存在姿现,若有,則寫者應(yīng)阻塞肖抱;
  3. 寫备典、寫之間互斥
    這條規(guī)則的含義是只要有寫者在共享區(qū)內(nèi)執(zhí)行寫操作,其它的寫者就必須在共享區(qū)外等待意述,直到當(dāng)前寫者退出后提佣,其它寫者才有機(jī)會(huì)。
    (1)當(dāng)寫者進(jìn)入共享區(qū)荤崇,且檢查了當(dāng)前共享區(qū)內(nèi)沒有讀者存在镐依,應(yīng)再檢查共享區(qū)內(nèi)是否有寫者在寫,若有天试,寫者應(yīng)阻塞槐壳;若沒有,寫者可直接進(jìn)入共享區(qū)喜每;
  4. 讀务唐、讀之間共享
    這條規(guī)則的含義是即便當(dāng)前共享區(qū)內(nèi)有讀者在讀,其它讀者也是可以進(jìn)入到共享區(qū)內(nèi)的带兜,而不必阻塞枫笛。
    (1)當(dāng)讀者要進(jìn)入數(shù)據(jù)共享區(qū)時(shí),如果沒有寫者阻塞和寫者在進(jìn)行寫操作刚照,則可以直接進(jìn)入共享區(qū)刑巧;
讀寫鎖的加鎖、解鎖流程
  1. 讀加鎖流程
    讀者在進(jìn)入數(shù)據(jù)共享區(qū)時(shí),應(yīng)首先檢查是否有寫者阻塞或共享區(qū)內(nèi)寫者是否在寫啊楚,兩者只要有其一吠冤,讀者阻塞數(shù)量應(yīng)累加,且當(dāng)前讀者應(yīng)阻塞恭理;若兩者皆沒有拯辙,讀者將共享區(qū)內(nèi)的讀者數(shù)目累加,然后直接進(jìn)入共享區(qū)颜价;
  2. 讀解鎖流程
    讀者在離開數(shù)據(jù)共享區(qū)時(shí)涯保,共享區(qū)內(nèi)的讀者數(shù)目減1,并判斷值是否為0周伦,若是夕春,接著判斷是否有寫者在阻塞,若有专挪,則直接喚醒某個(gè)寫者撇他;若沒有寫者阻塞或共享區(qū)內(nèi)的讀者數(shù)目不為0,則直接退出共享區(qū)(注意:當(dāng)沒有寫者在寫或沒有寫者阻塞時(shí)狈蚤,讀者是不會(huì)阻塞的)困肩;
  3. 寫加鎖流程
    寫者在進(jìn)入數(shù)據(jù)共享區(qū)時(shí),首先判斷當(dāng)前共享區(qū)內(nèi)是否有讀者存在脆侮,若有锌畸,寫者阻塞數(shù)目累加,然后寫者阻塞靖避;若沒有潭枣,再接著判斷共享區(qū)內(nèi)是否有寫者在寫,若有幻捏,寫者阻塞數(shù)目累加盆犁,然后寫者阻塞;若沒有篡九,寫者將寫標(biāo)志置位谐岁,并進(jìn)入共享區(qū);
  4. 寫解鎖流程
    寫者在離開數(shù)據(jù)共享區(qū)時(shí)榛臼,首先將寫標(biāo)志清零伊佃,接著判斷是否有寫者在阻塞,若有沛善,則喚醒某個(gè)寫者航揉;若沒有,再判斷是否有讀者在阻塞金刁,若有帅涂,則喚醒所有讀者议薪;若沒有,則直接離開共享區(qū)媳友;
讀寫鎖的實(shí)現(xiàn)

經(jīng)過上述分析斯议,在實(shí)現(xiàn)讀寫鎖時(shí),需要記錄“存在于共享區(qū)內(nèi)在讀的讀者數(shù)量”庆锦、“讀者阻塞的數(shù)量”、“寫者阻塞的數(shù)量”轧葛、“寫者是否在寫共享區(qū)的標(biāo)志”搂抒,以及和讀者、寫者阻塞相關(guān)的兩個(gè)條件變量尿扯,進(jìn)入共享區(qū)的互斥鎖求晶。

typedef struct rw_lock_ {
  unsigned int on-readers;              //readers exists in sharing region
  unsigned int read_waiters;            //waiting readers
  unsigned int write_waiters;           //waiting writers
  unsigned int write_flag;              
  pthread_mutex_t mutex_lock;
  pthread_cond_t  rwait_cond;
  pthread_cond_t  wwait_cond;
} rw_lock_t;

int rw_lock_init(rw_lock_t* prw_lock)
{
  prw_lock->on-readers = 0;
  prw_lock->read_waiters = 0;
  prw_lock->write_waiters = 0;
  prw_lock->write_flag = 0;
  pthread_mutex_init(&prw_lock->mutex_lock, NULL);
  pthread_cond_init(&prw_lock->rwait_cond, NULL);
  pthread_cond_init(&prw_lock->wwait_cond, NULL);
  
  return 0;
}

int rw_lock_destroy(rw_lock_t* prw_lock)
{
  prw_lock->on-readers = 0;
  prw_lock->read_waiters = 0;
  prw_lock->write_waiters = 0;
  prw_lock->write_flag = 0;
  pthread_mutex_destroy(&prw_lock->mutex_lock);
  pthread_cond_destroy(&prw_lock->rwait_cond);
  pthread_cond_destroy(&prw_lock->wwait_cond);

  return 0;
}

int rw_lock_read_lock(rw_lock_t* prw_lock)
{
  pthread_mutex_lock(&prw_lock->mutex_lock);
  while(prw_lock->write_waiters > 0 || prw_lock->write_flag == 1)
  {
    prw_lock->read_waiters++;
    pthread_cond_wait(&prw_lock->rwait_cond, &prw_lock->mutex_lock);
    prw_lock->read_waiters--;
  }
  prw_lock->on-readers++;
  pthread_mutex_unlock(&prw_lock->mutex_lock);

  return 0;
}

int rw_lock_read_unlock(rw_lock_t* prw_lock)
{
  pthread_mutex_lock(&prw_lock->mutex_lock);
  prw_lock->on-readers--;
  if(prw_lock->on-readers == 0)
  {
    if(prw_lock->write_waiters > 0)
    {
      pthread_cond_signal(&prw_lock->rwait_cond);
    }
  }
  pthread_mutex_unlock(&prw_lock->mutex_lock);

  return 0;
}

int rw_lock_write_lock(rw_lock_t* prw_lock)
{
  pthread_mutex_lock(&prw_lock->mutex_lock);
  while(prw_lock->on-readers > 0 || prw_lock->write_flag == 1)
  {
    prw_lock->write_waiters++;
    pthread_cond_wait(&prw_lock->wwait_cond);
    prw_lock->write_waiters--;
  }
  prw_lock->write_flag = 1;
  pthread_mutex_unlock(&prw_lock->mutex_lock);

  return 0;
}

int rw_lock_write_unlock(rw_lock_t* prw_lock)
{
  pthread_mutex_lock(&prw_lock->mutex_lock);
  prw_lock->write_flag = 0;
  if(prw_lock->write_waiters > 0)
  {
    pthread_cond_signal(&prw_lock->wwait_cond);
  }
  else if(prw_lock->read_waiters > 0)
  {
    pthread_cond_broadcast(&prw_lock->rwait_cond);
  }
  pthread_mutex_unlock(&prw_lock->mutex_lock);

  return 0;
}

另有非阻塞讀鎖和非阻塞寫鎖,實(shí)現(xiàn)思路分別為:

  1. 非阻塞讀鎖:當(dāng)讀者進(jìn)入共享區(qū)時(shí)衷笋,如果檢查到有寫者阻塞或共享區(qū)的寫標(biāo)志置為1芳杏,則直接返回,而不是阻塞等待辟宗;若兩者均未檢測(cè)到爵赵,則直接進(jìn)入共享區(qū);
  2. 非阻塞寫鎖:當(dāng)寫者進(jìn)入共享區(qū)時(shí)泊脐,如果檢查到共享區(qū)內(nèi)有讀者在讀或有寫者在寫空幻,則直接返回,而不是阻塞等待容客;若兩者均未檢測(cè)到秕铛,則直接進(jìn)入共享區(qū);
int rw_lock_read_trylock(rw_lock_t* prw_lock)
{
  pthread_mutex_lock(&prw_lock->mutex_lock);
  if(prw_lock->write_waiters > 0 || prw_lock->write_flag == 1)
  {
   return -1;
  }
  prw_lock->on-readers++;
  pthread_mutex_unlock(&prw_lock->mutex_lock);

  return 0;
}

int rw_lock_write_trylock(rw_lock_t* prw_lock)
{
  pthread_mutex_lock(&prw_lock->mutex_lock);
  if(prw_lock->on-readers > 0 || prw_lock->write_flag == 1)
  {
    return -1;
  }
  prw_lock->write_flag = 1;
  pthread_mutex_unlock(&prw_lock->mutex_lock);

  return 0;
}

參考文章

1. ChinaUnix | 可不可以不取名 | 線程同步:條件變量的使用細(xì)節(jié)分析
2. Cnblogs | 寒莓 | 動(dòng)手實(shí)現(xiàn)讀寫鎖
3. Cnblogs | myd620 | 一步一步實(shí)現(xiàn)讀寫鎖
4. Cnblogs | Vamei | Linux多線程與同步
5. CSDN | LUCKYOJ | 實(shí)現(xiàn)線程讀寫鎖的四種方法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末但两,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子供置,更是在濱河造成了極大的恐慌谨湘,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芥丧,死亡現(xiàn)場(chǎng)離奇詭異悲关,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)娄柳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門寓辱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人赤拒,你說我怎么就攤上這事秫筏∮站希” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵这敬,是天一觀的道長(zhǎng)航夺。 經(jīng)常有香客問我,道長(zhǎng)崔涂,這世上最難降的妖魔是什么阳掐? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮冷蚂,結(jié)果婚禮上缭保,老公的妹妹穿的比我還像新娘。我一直安慰自己蝙茶,他們只是感情好艺骂,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著隆夯,像睡著了一般钳恕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蹄衷,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天忧额,我揣著相機(jī)與錄音,去河邊找鬼愧口。 笑死宙址,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的调卑。 我是一名探鬼主播抡砂,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼恬涧!你這毒婦竟也來了注益?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤溯捆,失蹤者是張志新(化名)和其女友劉穎丑搔,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體提揍,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡啤月,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了劳跃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谎仲。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖刨仑,靈堂內(nèi)的尸體忽然破棺而出郑诺,到底是詐尸還是另有隱情夹姥,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布辙诞,位于F島的核電站辙售,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏飞涂。R本人自食惡果不足惜旦部,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望较店。 院中可真熱鬧士八,春花似錦、人聲如沸泽西。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽捧杉。三九已至,卻和暖如春秘血,著一層夾襖步出監(jiān)牢的瞬間味抖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工灰粮, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留仔涩,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓粘舟,卻偏偏與公主長(zhǎng)得像熔脂,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子柑肴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

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

  • 引用自多線程編程指南應(yīng)用程序里面多個(gè)線程的存在引發(fā)了多個(gè)執(zhí)行線程安全訪問資源的潛在問題霞揉。兩個(gè)線程同時(shí)修改同一資源有...
    Mitchell閱讀 1,990評(píng)論 1 7
  • 轉(zhuǎn)自:Youtherhttps://www.cnblogs.com/youtherhome/archive/201...
    njukay閱讀 1,615評(píng)論 0 52
  • 線程 在linux內(nèi)核那一部分我們知道,線程其實(shí)就是一種特殊的進(jìn)程晰骑,只是他們共享進(jìn)程的文件和內(nèi)存等資源适秩,無論如何對(duì)...
    大雄good閱讀 668評(píng)論 0 2
  • 在上篇中秽荞,我們已經(jīng)討論過如何去實(shí)現(xiàn)一個(gè) Map 了,并且也討論了諸多優(yōu)化點(diǎn)抚官。在下篇中扬跋,我們將繼續(xù)討論如何實(shí)現(xiàn)一個(gè)線...
    一縷殤流化隱半邊冰霜閱讀 7,619評(píng)論 5 41
  • linux線程同步 信號(hào)燈:與互斥鎖和條件變量的主要不同在于"燈"的概念,燈亮則意味著資源可用凌节,燈滅則意味著不可用...
    鮑陳飛閱讀 686評(píng)論 0 2