PPL-并行模式庫

PPL簡介

并行模式庫 (PPL) 提供命令式編程模型,以促進(jìn)開發(fā)并發(fā)應(yīng)用程序的可擴(kuò)展性和易用性扇苞。 PPL 構(gòu)建在并發(fā)運(yùn)行時(shí)的計(jì)劃和資源管理組件上欺殿。
通過提供并行作用于數(shù)據(jù)的泛型安全算法和容器,提高應(yīng)用程序代碼與基礎(chǔ)線程機(jī)制之間的抽象級別鳖敷。 使用 PPL 還可以開發(fā)通過為共享狀態(tài)提供替代方案實(shí)現(xiàn)縮放的應(yīng)用程序脖苏。

PPL 提供以下功能:

  • 任務(wù)并行︰ 一種工作在Windows線程池上,可以并行地執(zhí)行多個(gè)任務(wù)的機(jī)制
  • 并行算法︰ 利用并發(fā)運(yùn)行時(shí)(Concurrency Runtime )定踱,并行處理數(shù)據(jù)集合的泛型算法
  • 并行容器和對象︰ 提供對其元素的安全并發(fā)訪問的泛型容器類型

并行算法

并行模式庫 (PPL) 提供了在數(shù)據(jù)集合并發(fā)執(zhí)行工作的算法棍潘。 這些算法類似于標(biāo)準(zhǔn)模板庫 (STL)所提供的。
并行算法由并發(fā)運(yùn)行時(shí)中的現(xiàn)有功能組成。 例如, concurrency:: parallel_for 算法使用 concurrency:: structured_task_group 對象執(zhí)行并行循環(huán)迭代飞袋。 parallel_for 算法以最佳方式工作分區(qū)給定可用數(shù)量的計(jì)算資源震叮。

parallel_for 算法

簡介

Concurrency:: parallel_for 算法重復(fù)地以并行方式執(zhí)行相同的任務(wù)汹胃。 每個(gè)任務(wù)是由迭代值參數(shù)化。 當(dāng)您具有不共享資源分布該循環(huán)的迭代循環(huán)主體時(shí),此算法很有用。
parallel_for 算法以可最佳并行執(zhí)行的方式對任務(wù)進(jìn)行分區(qū)赛蔫。 當(dāng)工作負(fù)載不平衡時(shí),此算法還會(huì)使用工作竊取算法和范圍竊取來平衡這些分區(qū)泥张。 當(dāng)某個(gè)循環(huán)迭代以協(xié)作方式被阻塞時(shí)呵恢,則運(yùn)行時(shí)將原本分配當(dāng)前線程的迭代范圍重新分配給其他線程或處理器。 同樣媚创,當(dāng)某個(gè)線程完成迭代范圍渗钉,則運(yùn)行時(shí)將原本屬于其他線程的工作重新分配給該線程。 parallel_for 算法還支持 嵌套并行钞钙。 當(dāng)一個(gè)并行循環(huán)中包含另一個(gè)并行循環(huán)時(shí)晌姚,運(yùn)行時(shí)之間以高效并行執(zhí)行的方式協(xié)調(diào)處理循環(huán)主體的資源粤剧。

parallel_for 算法有多個(gè)重載版本。 第一個(gè)版本采用起始值挥唠、 結(jié)束值和工作函數(shù) (lambda 表達(dá)式、 函數(shù)對象或函數(shù)指針)焕议。 第二個(gè)版本所依據(jù)采用起始值宝磨、 結(jié)束值、 一個(gè)步長值和工作函數(shù)盅安。 此函數(shù)的第一個(gè)版本使用 1 作為步長值唤锉。 其余版本采用分區(qū)程序?qū)ο螅鼓軌蛑付?parallel_for如何在線程之間對范圍進(jìn)行分區(qū)别瞭。 分區(qū)程序在 分區(qū)工作 中有更詳細(xì)地介紹窿祥。
您可以將許多 for 循環(huán),轉(zhuǎn)換成 parallel_for蝙寨。 但是晒衩, parallel_for 算法與 for 語句以下不同點(diǎn)︰

  • parallel_for 算法不再以預(yù)先確定的順序執(zhí)行這些任務(wù)。

  • parallel_for 算法不支持任意終止條件墙歪。 parallel_for 當(dāng)?shù)兞康漠?dāng)前值小于上一次時(shí)听系,并行算法就停止了。

  • _Index_type 類型參數(shù)必須是整數(shù)類型虹菲。 此整型可以有符號(hào)或無符號(hào)靠胜。

  • 循環(huán)迭代必須前滾。 如果 _Step 參數(shù)是小于 1毕源,parallel_for 算法將引發(fā)類型的異常 std::invalid_argument 浪漠。

  • parallel_for 算法的異常處理機(jī)制不同于 for 循環(huán)。 如果在并行循環(huán)主體中同時(shí)發(fā)生多個(gè)異常霎褐,運(yùn)行時(shí)僅會(huì)將一個(gè)異常傳播到調(diào)用parallel_for的線程址愿。 此外,當(dāng)某個(gè)循環(huán)迭代拋出異常瘩欺,則運(yùn)行時(shí)不會(huì)立即停止整個(gè)循環(huán)必盖。 相反,該循環(huán)處于已取消狀態(tài)俱饿,并且運(yùn)行時(shí)會(huì)丟棄尚未啟動(dòng)的任何任務(wù)歌粥。 有關(guān)異常處理和并行算法的詳細(xì)信息,請參閱 異常處理拍埠。

盡管 parallel_for 算法不支持任意終止條件失驶,但你可以使用取消以停止所有的任務(wù)。 有關(guān)取消的詳細(xì)信息枣购,請參閱 取消嬉探。

注意
對負(fù)載均衡和取消之類的功能的支持擦耀,可能會(huì)抵消并行執(zhí)行循環(huán)主體可能帶來的好處,尤其當(dāng)循環(huán)體相對較小時(shí)涩堤。
您可以在并行循環(huán)中使用分區(qū)程序來盡量減少此開銷眷蜓。 有關(guān)詳細(xì)信息,請參閱 分區(qū)工作 胎围。

示例

下面的示例演示的基本結(jié)構(gòu) parallel_for 算法吁系。 本示例向控制臺(tái)打印范圍 [1,5] 并行中的每個(gè)值白魂。

// parallel-for-structure.cpp
// compile with: /EHsc
#include <ppl.h>
#include <array>
#include <sstream>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
   // Print each value from 1 to 5 in parallel.
   parallel_for(1, 6, [](int value) {
      wstringstream ss;
      ss << value << L' ';
      wcout << ss.str();
   });
}

該示例產(chǎn)生下面的示例輸出︰
1 2 4 3 5

注意
因?yàn)?parallel_for 算法作用于并行每個(gè)項(xiàng)目汽纤,向控制臺(tái)打印值的順序?qū)?huì)不同。
有關(guān)完整的示例使用 parallel_for 算法福荸,請參閱 如何︰ 編寫 parallel_for 循環(huán)蕴坪。

parallel_for_each 算法

Concurrency:: parallel_for_each 算法對迭代容器(如 STL提供的)并行執(zhí)行任務(wù)。 它跟parallel_for 算法一樣使用相同的分區(qū)邏輯敬锐。

parallel_for_each 算法類似于 STL for_each 算法背传,不同之處在于 parallel_for_each 算法并發(fā)執(zhí)行任務(wù)。 像其他并行算法一樣滞造, parallel_for_each 不會(huì)按特定順序執(zhí)行這些任務(wù)续室。

盡管 parallel_for_each 算法支持前向迭代器和隨機(jī)訪問迭代器,但隨機(jī)訪問迭代器表現(xiàn)更好谒养。

示例

下面的示例演示parallel_for_each 算法的基本結(jié)構(gòu)挺狰。 此示例并行地將 std:: array對象中的每個(gè)值打印到控制臺(tái) 。

// parallel-for-each-structure.cpp
// compile with: /EHsc
#include <ppl.h>
#include <array>
#include <sstream>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
   // Create an array of integer values.
   array<int, 5> values = { 1, 2, 3, 4, 5 };

   // Print each value in the array in parallel.
   parallel_for_each(begin(values), end(values), [](int value) {
      wstringstream ss;
      ss << value << L' ';
      wcout << ss.str();
   });
}
/* Sample output:
   5 4 3 1 2
*/

輸出如下:
4 5 1 2 3

注意
因?yàn)?parallel_for_each 算法并行作用于每個(gè)元素买窟,向控制臺(tái)打印值的順序?qū)?huì)不同丰泊。
有關(guān)完整的示例使用 parallel_for_each 算法,請參閱 如何︰ 編寫 parallel_for_each 循環(huán)始绍。

parallel_invoke 算法

Concurrency:: parallel_invoke 算法以并行方式執(zhí)行一組任務(wù)瞳购。 每個(gè)任務(wù)完成之前不返回。 當(dāng)您想要在同一時(shí)間執(zhí)行多個(gè)獨(dú)立的任務(wù)時(shí)亏推,此算法很有用学赛。

parallel_invoke 算法將一系列工作函數(shù)作為其參數(shù) (lambda 函數(shù)、 函數(shù)對象或函數(shù)指針)吞杭。 parallel_invoke 算法被重載成以 2 到 10 個(gè)參數(shù)之間的形式盏浇。 傳遞給 parallel_invoke的每個(gè)函數(shù)必須都必須是不帶參的。

像其他并行算法 parallel_invoke 不會(huì)按特定順序執(zhí)行這些任務(wù)芽狗。 任務(wù)并行 解釋了parallel_invoke 算法如何關(guān)聯(lián)不同的任務(wù)和任務(wù)組绢掰。

示例

下面的示例演示 parallel_invoke 算法的基本結(jié)構(gòu)。 此示例用三個(gè)局部變量并發(fā)調(diào)用 twice函數(shù),并輸出結(jié)果到控制臺(tái)滴劲。

// parallel-invoke-structure.cpp
// compile with: /EHsc
#include <ppl.h>
#include <string>
#include <iostream>

using namespace concurrency;
using namespace std;

// Returns the result of adding a value to itself.
template <typename T>
T twice(const T& t) {
   return t + t;
}

int wmain()
{
   // Define several values.
   int n = 54;
   double d = 5.6;
   wstring s = L"Hello";

   // Call the twice function on each value concurrently.
   parallel_invoke(
      [&n] { n = twice(n); },
      [&d] { d = twice(d); },
      [&s] { s = twice(s); }
   );

   // Print the values to the console.
   wcout << n << L' ' << d << L' ' << s << endl;
}

輸出:
108 11.2 HelloHello

有關(guān)使用 parallel_invoke 算法的完整示例攻晒,請參閱 如何︰ 使用 parallel_invoke 來編寫并行排序例程如何︰ 使用 parallel_invoke 來執(zhí)行并行操作

parallel_transform 和 parallel_reduce 算法

Concurrency:: parallel_transformconcurrency:: parallel_reduce 算法分別是 STL 算法 std:: transformstd:: accumulate的并行版本班挖。 并發(fā)運(yùn)行時(shí)版本的行為類似于 STL 版本鲁捏,只不過因?yàn)樗鼈兪遣⑿袌?zhí)行的,所以操作順序不確定聪姿。 當(dāng)您使用的集合足夠大碴萧,可從并行處理中獲得性能和可擴(kuò)展性優(yōu)勢時(shí),請使用這些算法末购。
注意

因?yàn)檫@些迭代器會(huì)生成穩(wěn)定的內(nèi)存地址,所以 parallel_transform 算法和 parallel_reduce 算法僅支持隨機(jī)訪問虎谢、雙向和向前迭代器盟榴。 
而且這些迭代器必須生成非 const 左值。

parallel_transform 算法

您可以使用 parallel transform 算法執(zhí)行許多數(shù)據(jù)并行化操作婴噩。 例如擎场,你可以:

  • 調(diào)整圖像的亮度,并執(zhí)行其他圖像處理操作几莽。

  • 在兩個(gè)向量之間求和或取點(diǎn)積迅办,并對向量執(zhí)行其他數(shù)值計(jì)算。

  • 執(zhí)行三維射線跟蹤章蚣,其中每次迭代引用一個(gè)必須呈現(xiàn)的像素站欺。

下面的示例顯示用于調(diào)用 parallel_transform 算法的基本結(jié)構(gòu)。 此示例中采用兩種方式對std::vector對象中的每個(gè)元素求反纤垂。 第一種方法是使用 lambda 表達(dá)式矾策。 第二種方法是使用 std::negate, ,它派生自 std::unary_function峭沦。

// basic-parallel-transform.cpp
// compile with: /EHsc
#include <ppl.h>
#include <random>

using namespace concurrency;
using namespace std;

int wmain()
{
    // Create a large vector that contains random integer data.
    vector<int> values(1250000);
    generate(begin(values), end(values), mt19937(42));

    // Create a vector to hold the results.
    // Depending on your requirements, you can also transform the 
    // vector in-place.
    vector<int> results(values.size());

    // Negate each element in parallel.
    parallel_transform(begin(values), end(values), begin(results), [](int n) {
        return -n;
    });

    // Alternatively, use the negate class to perform the operation.
    parallel_transform(begin(values), end(values), begin(results), negate<int>());
}

注意
本示例演示 parallel_transform 的基本用法贾虽。 由于工作函數(shù)不會(huì)執(zhí)行大量工作,因此本示例中不會(huì)有顯著的性能提升吼鱼。

parallel_transform 算法有兩個(gè)重載蓬豁。 第一個(gè)重載采用一個(gè)輸入范圍和一個(gè)一元函數(shù)。 該一元函數(shù)可以是采用一個(gè)參數(shù)的 lambda 表達(dá)式菇肃、一個(gè)函數(shù)對象或從 unary_function 派生的一個(gè)類型地粪。 第二個(gè)重載采用兩個(gè)輸入范圍和一個(gè)二元函數(shù)。 該二元函數(shù)可以采用兩個(gè)參數(shù)巷送、 函數(shù)對象或派生自的類型的 lambda 表達(dá)式 std:: binary_function驶忌。 下面的示例闡釋了這些差異。

    //
    // Demonstrate use of parallel_transform together with a unary function.

    // This example uses a lambda expression.
    parallel_transform(begin(values), end(values), 
        begin(results), [](int n) { 
            return -n;
        });

    // Alternatively, use the negate class:
    parallel_transform(begin(values), end(values), 
        begin(results), negate<int>());

    //
    // Demonstrate use of parallel_transform together with a binary function.

    // This example uses a lambda expression.
    parallel_transform(begin(values), end(values), begin(results), 
        begin(results), [](int n, int m) {
            return n * m;
        });

    // Alternatively, use the multiplies class:
    parallel_transform(begin(values), end(values), begin(results), 
        begin(results), multiplies<int>());

注意
您為 parallel_transform 的輸出提供的迭代器必須與輸入迭代器完全重疊或根本不重疊。 如果輸入迭代器和輸出迭代器部分重疊付魔,則此算法的行為是未指定的聊品。

parallel_reduce 算法

當(dāng)您具有滿足關(guān)聯(lián)屬性的操作序列時(shí),parallel_reduce 算法很有用几苍。 (此算法不要求可交換屬性翻屈。)以下是可以使用 parallel_reduce 執(zhí)行的一些操作:

  • 將矩陣的序列相乘以生成一個(gè)矩陣。

  • 用矩陣序列乘以一個(gè)向量來生成一個(gè)向量妻坝。

  • 計(jì)算字符串序列的長度伸眶。

  • 將一個(gè)元素列表(例如字符串)組合為一個(gè)元素。

下面的基本示例演示如何使用 parallel_reduce 算法將一個(gè)字符串序列組合為一個(gè)字符串刽宪。 與 parallel_transform 的示例一樣厘贼,此基本示例中不會(huì)有性能提升。

// basic-parallel-reduce.cpp
// compile with: /EHsc
#include <ppl.h>
#include <iostream>
#include <string> 
#include <vector>

using namespace concurrency;
using namespace std;

int wmain()
{
    // Create a vector of strings.
    vector<wstring> words;
    words.push_back(L"Lorem ");
    words.push_back(L"ipsum ");
    words.push_back(L"dolor ");
    words.push_back(L"sit ");
    words.push_back(L"amet, ");
    words.push_back(L"consectetur ");
    words.push_back(L"adipiscing ");
    words.push_back(L"elit.");

    // Reduce the vector to one string in parallel.
    wcout << parallel_reduce(begin(words), end(words), wstring()) << endl;
}

/* Output:
   Lorem ipsum dolor sit amet, consectetur adipiscing elit.
*/

在許多情況下圣拄,您可以將 parallel_reduce 當(dāng)成是parallel_for_eachconcurrency:: combinable 類一起使用的的簡寫形式嘴秸。

示例︰ 并行執(zhí)行映射和歸約

一個(gè) 映射 操作將函數(shù)應(yīng)用于序列中的每個(gè)值。 一個(gè) 歸約 操作會(huì)將序列中的每一個(gè)元素組合成一個(gè)值庇谆。 可以使用標(biāo)準(zhǔn)模板庫 (STL) std:: transformstd:: accumulate 類來執(zhí)行映射和歸約操作岳掐。 但是,對于許多問題饭耳,您可以使用 parallel_transform 算法并行執(zhí)行映射操作串述,并使用 parallel_reduce 算法并行執(zhí)行歸約操作。

下面的示例將按串行方式計(jì)算質(zhì)數(shù)和所需的時(shí)間與按并行方式計(jì)算質(zhì)數(shù)和所需的時(shí)間進(jìn)行比較寞肖。 映射階段會(huì)將非質(zhì)數(shù)值轉(zhuǎn)換為 0纲酗,而歸約階段將對這些值求和。

// parallel-map-reduce-sum-of-primes.cpp
// compile with: /EHsc
#include <windows.h>
#include <ppl.h>
#include <array>
#include <numeric>
#include <iostream>

using namespace concurrency;
using namespace std;

// Calls the provided work function and returns the number of milliseconds 
// that it takes to call that function.
template <class Function>
__int64 time_call(Function&& f)
{
   __int64 begin = GetTickCount();
   f();
   return GetTickCount() - begin;
}

// Determines whether the input value is prime.
bool is_prime(int n)
{
   if (n < 2)
      return false;
   for (int i = 2; i < n; ++i)
   {
      if ((n % i) == 0)
         return false;
   }
   return true;
}

int wmain()
{   
   // Create an array object that contains 200000 integers.
   array<int, 200000> a;

   // Initialize the array such that a[i] == i.
   iota(begin(a), end(a), 0);

   int prime_sum;
   __int64 elapsed;

   // Compute the sum of the numbers in the array that are prime.
   elapsed = time_call([&] {
       transform(begin(a), end(a), begin(a), [](int i) { 
         return is_prime(i) ? i : 0; 
      });
      prime_sum = accumulate(begin(a), end(a), 0);
   });   
   wcout << prime_sum << endl;   
   wcout << L"serial time: " << elapsed << L" ms" << endl << endl;

   // Now perform the same task in parallel.
   elapsed = time_call([&] {
      parallel_transform(begin(a), end(a), begin(a), [](int i) { 
         return is_prime(i) ? i : 0; 
      });
      prime_sum = parallel_reduce(begin(a), end(a), 0);
   });
   wcout << prime_sum << endl;
   wcout << L"parallel time: " << elapsed << L" ms" << endl << endl;
}
/* Sample output:
   1709600813
   serial time: 7406 ms
   
   1709600813
   parallel time: 1969 ms
*/

注意
有關(guān)執(zhí)行映射和化簡操作的并行的另一個(gè)示例逝淹,請參閱 如何︰ 并行執(zhí)行映射和歸約操作耕姊。

分區(qū)工作

若要在數(shù)據(jù)源上并行化一個(gè)操作,不可或缺的步驟為將操作源 分區(qū)成可被多線程并發(fā)操作的幾塊栅葡。 分區(qū)程序將指定并行算法如何在線程間劃分區(qū)間范圍茉兰。 如本文檔前面所述,PPL 使用的是默認(rèn)分區(qū)機(jī)制欣簇,該默認(rèn)分區(qū)機(jī)制創(chuàng)建初始工作負(fù)荷并在工作負(fù)荷不平衡時(shí)使用工作竊取算法和范圍竊取來平衡這些分區(qū)规脸。 例如,當(dāng)某個(gè)循環(huán)迭代完成一個(gè)迭代范圍時(shí)熊咽,運(yùn)行時(shí)會(huì)將其他線程的工作重新分配給該線程莫鸭。 但是,在某些方案中横殴,您可能希望指定另一個(gè)更適用于您的問題的分區(qū)機(jī)制被因。

parallel_for卿拴、parallel_for_eachparallel_transform 算法提供有一附加參數(shù) _Partitioner 的重載版本。 此參數(shù)定義了用于劃分工作的分區(qū)程序類型梨与。 以下是 PPL 定義的分區(qū)程序種類:

concurrency::affinity_partitioner
將工作劃分為一個(gè)固定數(shù)量的范圍(通常是可用于在循環(huán)中工作的輔助線程的數(shù)量)堕花。 此分區(qū)程序類型與 static_partitioner 類似,但通過將范圍映射到輔助線程的方式改善了緩存的關(guān)聯(lián)粥鞋。 當(dāng)在相同數(shù)據(jù)集中多次執(zhí)行一個(gè)循環(huán)(例如一個(gè)循環(huán)內(nèi)的循環(huán))且數(shù)據(jù)適合緩存時(shí)缘挽,此分區(qū)程序類型可提高性能。 此分區(qū)程序不完全適用取消呻粹。 它也不使用協(xié)作阻塞語義壕曼,因此不能與具有前向依賴關(guān)系的并行循環(huán)一起使用。

concurrency::auto_partitioner
將工作劃分為一個(gè)初始數(shù)量的范圍(通常是可用于在循環(huán)中工作的輔助線程的數(shù)量)等浊。 當(dāng)您不調(diào)用采用 _Partitioner 參數(shù)的重載的并行算法時(shí)腮郊,運(yùn)行時(shí)默認(rèn)使用此類型。 每個(gè)范圍可以劃分為子范圍筹燕,從而實(shí)現(xiàn)負(fù)載平衡伴榔。 當(dāng)一個(gè)工作范圍完成時(shí),運(yùn)行時(shí)會(huì)將其他線程工作的子范圍重新分配給該線程庄萎。 如果您的工作負(fù)荷不在另外一個(gè)類別下或者您需要完全支持取消或協(xié)作阻塞,請使用該分區(qū)程序塘安。

concurrency::simple_partitioner
將工作劃分到范圍中糠涛,使每個(gè)范圍至少擁有給定區(qū)塊大小所指定的迭代的數(shù)目。 此分區(qū)程序類型加入了負(fù)載平衡兼犯;然而忍捡,運(yùn)行時(shí)未將范圍劃分為子范圍。 對于每個(gè)輔助切黔,運(yùn)行時(shí)將在 _Chunk_size 迭代完成后檢查取消情況并執(zhí)行負(fù)載平衡砸脊。

concurrency::static_partitioner
將工作劃分為一個(gè)固定數(shù)量的范圍(通常是可用于在循環(huán)中工作的輔助線程的數(shù)量)。 此分區(qū)程序類型不使用工作竊取纬霞,開銷較小凌埂,可以一定程度上提高性能。當(dāng)一個(gè)并行循環(huán)的每次迭代執(zhí)行固定和統(tǒng)一數(shù)量的工作而且您不需要支持取消或前向協(xié)作阻塞時(shí)诗芜,請使用此分區(qū)程序類型瞳抓。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市伏恐,隨后出現(xiàn)的幾起案子孩哑,更是在濱河造成了極大的恐慌,老刑警劉巖翠桦,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件横蜒,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)丛晌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門仅炊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人茵乱,你說我怎么就攤上這事茂洒。” “怎么了瓶竭?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵督勺,是天一觀的道長。 經(jīng)常有香客問我斤贰,道長智哀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任荧恍,我火速辦了婚禮瓷叫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘送巡。我一直安慰自己摹菠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布骗爆。 她就那樣靜靜地躺著次氨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪摘投。 梳的紋絲不亂的頭發(fā)上煮寡,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天,我揣著相機(jī)與錄音犀呼,去河邊找鬼幸撕。 笑死,一個(gè)胖子當(dāng)著我的面吹牛外臂,可吹牛的內(nèi)容都是我干的坐儿。 我是一名探鬼主播,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼专钉,長吁一口氣:“原來是場噩夢啊……” “哼挑童!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起跃须,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤站叼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后菇民,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體尽楔,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡投储,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了阔馋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片玛荞。...
    茶點(diǎn)故事閱讀 38,646評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖呕寝,靈堂內(nèi)的尸體忽然破棺而出勋眯,到底是詐尸還是另有隱情,我是刑警寧澤下梢,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布客蹋,位于F島的核電站,受9級特大地震影響孽江,放射性物質(zhì)發(fā)生泄漏讶坯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一岗屏、第九天 我趴在偏房一處隱蔽的房頂上張望辆琅。 院中可真熱鬧,春花似錦这刷、人聲如沸婉烟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽隅很。三九已至,卻和暖如春率碾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背屋彪。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工所宰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人畜挥。 一個(gè)月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓仔粥,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蟹但。 傳聞我的和親對象是個(gè)殘疾皇子躯泰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評論 2 348

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

  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 29,339評論 8 265
  • 一直覺得自己對并發(fā)了解不夠深入,特別是看了《代碼整潔之道》覺得自己有必要好好學(xué)學(xué)并發(fā)編程华糖,因?yàn)樾阅芤彩呛饬看a整潔...
    尋海閱讀 1,703評論 0 5
  • 拿起手機(jī)麦向,點(diǎn)進(jìn)簡書,里面的美文一頁頁客叉,一張張诵竭,每次都像見到久違的老友话告,莫名的興奮。有時(shí)候卵慰,就會(huì)在想沙郭,什么時(shí)候...
    涼溫閱讀 421評論 0 0
  • 我是日記星球267號(hào)星寶寶,我正在參加日記星球第十期21天蛻變之旅裳朋,這是我的第64篇原創(chuàng)日記病线。 本說明天回鄭州的,...
    張小姐4134閱讀 314評論 8 5
  • 這些直指人心的圖片 你看懂幾張? 01 自棄者扶不起 自強(qiáng)者打不倒 02 這是選擇泛范,并非孤獨(dú) 03 04 現(xiàn)代社會(huì)...
    零一間閱讀 443評論 0 1