iOS多線程-GCD

很久前的總結,今天貼出來。適合看了就用,很少講解,純粹用法。

目錄

Dispatch Queue

dispatch_queue_create

系統標準提供的Dispatch Queue

dispatch_set_target_queue

dispatch_after

Dispatch Group

dispatch_barrier_async

dispatch_sync

dispatch_apply

dispatch_suspend和dispatch_resume

Dispatch Semaphore

dispatch_once



Dsipatch Queue

官方說明:開發者要做的只是定義想要執行的任務并追加到適當的Dispatch?Queue中。

????這句話用如下代碼表示:

????dispatch_async(queue,?^{//想要執行的任務});

????即:使用block“定義想要執行的任務”,通過dispatch_async函數“追加”到賦值在queue的“Dispatch?Queue”中,這樣既可使指定的block在另一線程中運行。


簡介:Dispatch?Queue?是執行處理的等待隊列。通過dispatch_async函數等API,在block中記述想要執行的處理并將其追加到Dispatch?Queue中。Dispatch?Queue按照追加的順序執行處理。

分類:

????Serial?Dispatch?Queue:等待現在執行中的處理。

????Concurrent?Dispatch?Queue:不等待現在執行中的處理。

比較:

????Serial?Dispatch?Queue:因為等待現在執行中的處理結束,所以后一個等待前一個處理執行結束后才開始執行,同時執行的處理數只有一個。

????Concurrent?Dispatch?Queue:因為不等待現在執行中的處理結束,所以由前到后依次開始執行,即并發(使用多個線程同時)執行多個處理。注:并發執行的處理數取決于當前系統的狀態。

附:

iOS和OSX的核心一一XNU內核決定應當使用的線程數,并只生成所需的線程執行處理。另外,當執行結束,應當執行的處理減少時,XNU內核會結束不再需要的線程。XNU內核僅使用Concurrent?Dispatch?Queue?便可完美的管理并執行多個處理的線程。



dispatch_queue_create

//雖然Serial Dispatch Queue和Concurrent Dispatch Queue受到系統資源的限制,但用dispatch_queue_create函數可生成任意多個Dispatch Queue。

? ? //在一個Serial Dispatch Queue中同時只能執行一個追加處理,多個Serial Dispatch Queue可并行執行

? ? //過多使用Serial Dispatch Queue,會消耗大量內存,引起大量上下文切換,大幅度降低系統的響應性能,因此注意Serial Dispatch Queue的生成個數。

? ? //Concurrent Dispatch Queue不管生成多少,由于XNU內核只使用有效管理的線程,因此不會發生Serial Dispatch Queue的問題。

? ? //多個線程更新相同資源導致的數據競問題,使用Serial Dispatch Queue。

? ? //當并行執行不發生數據競爭等問題的處理時,使用Concurrent Dispatch Queue。


? ? //通過dispatch_queue_create可生成Dispatch Queue,注意,生成的Dispatch Queue在使用結束后必須由程序員進行釋放(現在GCD對象已經納入了ARC的管理范圍,在納入之前是需要程序員按照內存管理的思考方式來手動管理GCD對象的)。

? ? dispatch_queue_t serialDispatchQueue = dispatch_queue_create("Dispatch Queue的名稱", NULL);

? ? //參數1:Dispatch Queue的名稱,可設NULL

? ? //參數2:指定Dispatch Queue的類型。若為NULL,則為Serial Dispatch Queue;若為DISPATCH_QUEUE_CONCURRENT,則為Concurrent Dispatch Queue

? ? //返回值:表示Dispatch Queue的diapatch_queue_t類型


? ? //通過dispatch_release函數釋放

? ? //dispatch_release(serialDispatchQueue);


? ? //通過dispatch_retain函數持有

? ? //dispatch_retain(serialDispatchQueue);


? ? //通過dispatch_async函數將block中定義的處理“追加”到賦值在serialDispatchQueue的“Dispatch Queue”中

? ? dispatch_async(serialDispatchQueue, ^{

? ? ? ? NSLog(@"http://被追加的定義處理的block");

? ? });

? ? //注意,在dispatch_async函數中追加Block到Dispatch Queue,該Block通過dispatch_retain函數持有Dispatch Queue。無論Dispatch Queue是Serial Dispatch Queue還是Concurrent Dispatch Queue都一樣。一旦Block執行結束,就通過dispatch_release函數釋放該Block持有的Dispatch Queue。



系統標準提供的Dispatch Queue

//Main Dispatch Queue,是在主線程中執行的Dispatch Queue。Main Dispatch Queue是Serial Dispatch Queue。追加到Main Dispatch Queue的處理在主線程的RunLoop中執行。由于Main Dispatch Queue在主線程中執行,因此要將用戶界面的界面更新等一些必須在主線程中執行的處理追加到Main Dispatch Queue使用。

? ? //Global Dispatch Queue,是所有的應用程序都能夠使用的Concurrent Dispatch Queue。沒有必要通過dispatch_queue_create函數逐個生成Concurrent Dispatch Queue。只要獲取Global Dispatch Queue即可。Global Dispatch Queue有4個執行優先級,分別是高優先級(High Priority)、默認優先級(Default Priority)、低優先級(Low Priority)和后臺優先級(Background Priority)。用于Global Dispatch Queue的線程的執行優先級,使用Global Dispatch Queue的執行優先級。線程的執行優先級不能保證實時性,只是大致的判斷。


? ? //Main Dispatch Queue的獲取方法

? ? dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();

? ? //Global Dispatch Queue(高優先級)的獲取方法

? ? dispatch_queue_t globalDispatchQueueHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

? ? //Global Dispatch Queue(默認優先級)的獲取方法

? ? dispatch_queue_t globalDispatchQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

? ? //Global Dispatch Queue(低優先級)的獲取方法

? ? dispatch_queue_t globalDispatchQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW , 0);

? ? //Global Dispatch Queue(后臺優先級)的獲取方法

? ? dispatch_queue_t globalDispatchQueueBackgroud = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND , 0);


? ? //另外,對于Main Dispatch Queue和Global Dispatch Queue執行dispatch_release函數和dispatch和_retain函數不會引起任何變化,也不會有任何問題。但為了符合內存管理的思考方式,可以直接對Main Dispatch Queue和Global Dispatch Queue執行dispatch_release函數和dispatch和_retain函數。



dispatch_set_target_queue

//dispatch_queue_create函數生成的Dispatch Queue不管是Serial Dispatch Queue還是Concurrent Dispatch Queue,都使用與默認優先級Global Dispatch Queue相同執行優先級的線程。


? ? //通過dispatch_set_target_queue函數變更生成的Dispatch Queue的優先級。

? ? dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("Dispatch Queue的名稱", NULL);

? ? dispatch_queue_t globalDispatchQueueBackgroud = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND , 0);

? ? dispatch_set_target_queue(mySerialDispatchQueue, globalDispatchQueueBackgroud);

? ? //參數1:指定要變更執行優先級的Dispatch Queue

? ? //參數2:指定與要變更為的執行優先級相同的Dispatch Queue,即目標。

? ? //注:第一個參數不能指定為系統標準提供的Dispatch Queue。


? ? //通過dispatch_set_target_queue防止處理并行執行

? ? //如果在多個Serial Dispatch Queue中用dispatch_set_target_queue函數指定目標為某一個Serial Dispatch Queue,那么原先本應并行執行的多個Serial Dispatch Queue,在目標Serial Dispatch Queue上只能同時執行一個處理。



dispatch_after

//想在指定時間后執行處理的情況,可使用dispatch_after函數來實現。

? ? //dispatch_after函數并不是在指定的時間后執行處理,而只是在指定的時間追加處理到Dispatch Queue。


? ? //通過dispatch_after在3秒后將指定的block追加到Main Dispatch Queue

? ? dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW,3ull*NSEC_PER_SEC);

? ? dispatch_after(time, dispatch_get_main_queue(), ^{

? ? ? ? NSLog(@"1秒后");

? ? });

? ? //參數1:指定時間用的dispatch_time_t類型的值,該值使用dispatch_time函數或dispatch_walltime函數作成。

? ? //參數2:指定要追加處理的Dispatch Queue

? ? //參數3:指定記述要執行處理的Block

? ? //注:因為Main Dispatch Queue在主線程的RunLoop中執行,所以在比如每隔1/60秒執行的RunLoop中,Block最快在3秒后執行,最慢在3秒+1/60秒后執行,并且在Main Dispatch Queue有大量處理追加或主線程的處理本身有延遲時,這個時間會更長。雖然在有嚴格時間的要求下使用時會出現問題,但在想大致延遲執行處理時,該函數是非常有效的。


? ? //dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW,3ull*NSEC_PER_SEC);

? ? //dispatch_time函數能夠獲取從第一個參數dispatch_time_t類型值中指定的時間開始,到第二個參數指定的毫微秒單位時間后的時間。DISPATCH_TIME_NOW表示現在的時間。第二個參數中用秒和NSEC_PER_SEC的相乘來表示,毫秒和NSEC_PER_MSEC相乘來表示。ull即unsigned long long


? ? //dispatch_walltime函數用于計算絕對時間,如某個具體的時間。而dispatch_time通常用于計算相對時間。



Dispatch Group

//在追加到Dispatch Queue中的多個處理全部結束后想執行結束處理時:

? ? //a.只使用一個Serial Dispatch Queue時,只要將想執行的處理全部追加到該 Serial Dispatch Queue中并在最后追加結束處理,即可實現。

? ? //b.使用Concurrent Dispatch Queue時或同時使用多個Dispatch Queue時,可通過Dispatch Group實現。

? ? //無論向什么樣的Dispatch Queue中追加處理,使用Dispatch Group都可監視這些處理執行的結果。一旦檢測到所有處理執行結束,就可將結束的處理追加到Dispatch Queue中。


? ? dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);


? ? //通過dispatch_group_create函數生成Dispatch Group,注意,生成的Dispatch Group在使用結束后必須通過dispatch_release釋放(現在GCD對象已經納入了ARC的管理范圍,在納入之前是需要程序員按照內存管理的思考方式來手動管理GCD對象的),同Dispatch Queue。

? ? dispatch_group_t group = dispatch_group_create();


? ? //通過dispatch_group_async函數追加block到指定的Dispatch Queue中,第一個參數為監視的Dispatch Group。

? ? //注:與追加Block到Dispatch Queue時同樣,Block通過dispatch_retain函數持有Dispatch Group,如果Block執行結束,該Block就通過dispatch_release函數釋放持有的Dispatch Group。一旦Dispatch Group使用結束,不用考慮屬于該Dispatch Group的Block,立即通過dispatch_release函數釋放即可。

? ? dispatch_group_async(group, queue, ^{

? ? ? ? NSLog(@"blk0");

? ? });

? ? dispatch_group_async(group, queue, ^{

? ? ? ? NSLog(@"blk1");

? ? });

? ? dispatch_group_async(group, queue, ^{

? ? ? ? NSLog(@"blk2");

? ? });


? ? //通過dispatch_group_notify函數,在追加到Dispatch Queue中(即被Dispatch Group監測)的處理全部執行結束時,會將執行結束處理的Block追加到Dispatch Queue中

? ? //參數1:指定為要監視的Dispatch Group。

? ? //參數2:指定要追加到的Dispatch Queue。

? ? //參數3:結束處理的block

? ? dispatch_group_notify(group, queue, ^{

? ? ? ? NSLog(@"結束處理");

? ? });


? ? //也可以使用dispatch_group_wait函數僅等待全部處理執行結束

? ? //等待:意味著一旦調用dispatch_group_wait函數,該函數就處于調用的狀態而不返回。執行dispatch_group_wait函數的現在的線程(當前線程)停止。在經過dispatch_group_wait函數中指定的時間或屬于指定Dispatch Group的處理全部執行結束之前,執行該函數的線程停止。

? ? dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

? ? //參數1:指定Dispatch Group

? ? //參數2:指定最多等待的時間(超時),dispatch_time_t類型值

? ? //返回值:若等待的全部處理執行結束,返回0。否則,返回不為0。


? ? //當參數2為指定等待1秒時:

? ? dispatch_time_t time2 = dispatch_time(DISPATCH_TIME_NOW, 1ull*NSEC_PER_SEC);

? ? long result = dispatch_group_wait(group, time2);


? ? if (result == 0) {

? ? ? ? //若全部處理執行結束,返回值為0

? ? }else{

? ? ? ? //若超時,即還有處理在執行,則返回值不為0

? ? }

? ? //當參數2為DISPATCH_TIME_FORVER,意味著永久等待。只要屬于Dispatch Group的處理尚未執行結束,就會一直等待,中途不能取消。因此dispatch_group_wait函數返回時,由于屬于Dispatch Group的處理必定全部執行結束,因此返回值為0。

? ? //當參數2為DISPATCH_TIME_NOW,則不用任何等待即可判定屬于Dispatch Group的處理是否執行結束



dispatch_barrier_async

//寫入處理不可以與寫入處理和讀取處理并行執行。讀取處理與讀取處理可并行執行


? ? //為了高效率的進行訪問:

? ? //讀取處理追加到Concurrent Dispatch Queue中,寫入處理在任一個讀取處理沒有執行的狀態下,追加到Serial Dispatch Queue中即可,在寫入處理結束之前,讀取處理不可執行。

? ? //通過dispatch_barrier_async也可實現

? ? dispatch_queue_t queue = dispatch_queue_create("Dispatch Queue的名稱", DISPATCH_QUEUE_CONCURRENT);

? ? dispatch_async(queue, ^{/*讀取處理0*/});

? ? dispatch_async(queue, ^{/*讀取處理1*/});

? ? dispatch_async(queue, ^{/*讀取處理2*/});

? ? dispatch_async(queue, ^{/*讀取處理3*/});

? ? //寫入處理,之后會對寫入的內容進行讀取處理


? //若簡單的在dispatch_async函數中加入寫入處理,那么根據Concurrent Dispatch Queue的性質,就有可能在追加到寫入處理前面的處理中讀取到與期待不符的數據,還可能因非法訪問導致應用程序異常結束。如果追加多個寫入處理,則可能發生更多問題,比如數據競爭。

? ? //通過dispatch_barrier_async函數會等待追加到Concurrent Dispatch Queue上的并行執行的處理全部結束之后,再將指定的處理(異步)追加到該Concurrent Dispatch Queue中。然后在由dispatch_barrier_async函數追加的處理執行完畢后,Concurrent Dispatch Queue才恢復為一般的動作,追加到該Concurrent Dispatch Queue的處理又開始并行執行。

? ? dispatch_barrier_async(queue, ^{/*寫入處理*/});


? ? dispatch_async(queue, ^{/*讀取處理4*/});

? ? dispatch_async(queue, ^{/*讀取處理5*/});

? ? dispatch_async(queue, ^{/*讀取處理6*/});

? ? dispatch_async(queue, ^{/*讀取處理7*/});


? ? //dispatch_barrier_sync函數與dispatch_barrier_async不同的地方是它會將指定的block同步追加到Concurrent Dispatch Queue中。在追加Block結束之前,dispatch_barrier_sync函數會一直等待。即dispatch_barrier_async不阻塞當前線程,dispatch_barrier_sync阻塞當前線程。

參考資料:http://blog.csdn.net/u013046795/article/details/47057585



dispatch_sync

//dispatch_async函數將指定的Block"非同步"的追加到指定的Dispatch Queue中,dispatch_async函數不作任何等待。


? ? //dispatch_sync函數將指定的Block"同步"追加到指定的Dispatch Queue中。在追加Block結束之前,dispatch_sync函數會一直等待。即一旦調用dispatch_sync函數,那么在指定的處理執行結束之前,該函數不會返回。


? ? //dispatch_sync函數出現的死鎖問題:

? ? //a.該源代碼在Main Dispatch Queue即主線程中執行指定的Block,并等待其執行結束。而其實主線程中正在執行這些源代碼,所以無法執行追加到Main Dispatch Queue 的Block。

? ? dispatch_queue_t queue = dispatch_get_main_queue();

? ? dispatch_sync(queue, ^{

? ? ? ? NSLog(@"hello");

? ? });

? ? //b.Main Dispatch Queue中執行的Block等待Main Dispatch Queue中要執行的Block執行結束

? ? dispatch_queue_t queue2 = dispatch_get_main_queue();

? ? dispatch_async(queue2, ^{

? ? ? ? dispatch_sync(queue2,^{

? ? ? ? ? ? NSLog(@"hello");

? ? ? ? });

? ? });

? ? //c.Serial Dispatch Queue 也會引起相同的問題

? ? dispatch_queue_t queue3 = dispatch_queue_create("Dispatch Queue的名稱", NULL);

? ? dispatch_async(queue3, ^{

? ? ? ? dispatch_sync(queue3, ^{

? ? ? ? ? ? NSLog(@"hello");

? ? ? ? });

? ? });



dispatch_apply

//dispatch_apply函數按指定的次數將指定的Block追加到指定的Dispatch Queue中,并等待追加的全部處理執行結束。

? ? dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

? ? dispatch_apply(10, queue, ^(size_t index) {

? ? ? ? NSLog(@"%zu",index);

? ? });

? ? NSLog(@"done");

? ? //參數1:重復次數

? ? //參數2:指定要追加處理的Dispatch Queue

? ? //參數3:指定記述要執行處理的Block

? ? //注,參數3為帶有參數的block,這是為了按第一個參數重復追加Block并區分各個Block而使用


? ? //建議:由于dispatch_apply函數也與dispatch_sync函數相同,會等待處理執行結束,因此推薦在dispatch_async函數中非同步地執行dispatch_apply函數。

? ? dispatch_queue_t queue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

? ? dispatch_async(queue2, ^{

? ? ? ? dispatch_apply(10, queue2, ^(size_t index) {

? ? ? ? ? ? NSLog(@"%zu",index);

? ? ? ? });

? ? ? ? dispatch_async(dispatch_get_main_queue(), ^{

? ? ? ? ? ? NSLog(@"done");

? ? ? ? });

? ? });



dispatch_suspend和dispatch_resume

dispatch_queue_t queue = dispatch_queue_create("Dispatch Queue的名稱", NULL);


? ? //dispatch_suspend函數掛起指定的Dispatch Queue。

? ? dispatch_suspend(queue);

? ? //掛起后,追加到Dispatch Queue中但尚未執行的處理在此之后停止執行。


? ? //dispatch_resume函數恢復指定的Dispatch Queue。

? ? dispatch_resume(queue);

? ? //恢復則使得這些處理(追加到Dispatch Queue中但尚未執行的處理)能夠繼續執行。


? ? //注,這些函數對已經執行的處理沒有影響。



Dispatch Semaphore

//Dispatch Semaphore是持有計數的信號。計數為0時等待,計數為1或大于1時,減去1而不等待。


? ? //通過dispatch_semaphore_create函數生成Dispatch Semaphore。注意,生成的Dispatch Semaphore在使用結束后必須由程序員進行釋放(現在GCD對象已經納入了ARC的管理范圍,在納入之前是需要程序員按照內存管理的思考方式來手動管理GCD對象的),同Dispatch Queue。

? ? dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

? ? //參數1:表示計數的初始值。本例將計數值初始化為"1"。


? ? //通過dispatch_semaphore_wait函數等待Dispatch Semaphore的計數值大于或等于1。當計數值大于等于1,或者在待機中計數值大于等于1時,對該計數進行減法并從dispatch_semaphore_wait函數返回。

? ? dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

? ? //參數1:等待的Dispatch Semaphore

? ? //參數2:與dispatch_group_wait函數等相同,指最多等待的時間(超時)。

? ? //返回:若計數值大于等于1,返回值為0。否則,返回值不為0。


? ? //通過返回值進行分支處理

? ? dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull*NSEC_PER_SEC);

? ? long result = dispatch_semaphore_wait(semaphore, time);

? ? if (result == 0) {

? ? ? ? /*

? ? ? ? *? Dispatch Semaphore 的計數值達到大于等于1

? ? ? ? *? 或者在待機中的指定時間內Dispatch Semaphore的計數值達到大于等于1

? ? ? ? *? Dispatch Semaphore 的計數值減去1

? ? ? ? */

? ? }else{

? ? ? ? /*

? ? ? ? *? Dispatch Semaphore 的計數值為0

? ? ? ? */

? ? }


? ? //實際應用的例子

? ? dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

? ? //生成Dispatch Semaphore,Dispatch Semaphore的計數初始值設定為"1",保證可訪問NSMutableArray類對象的線程同時只能有一個。

? ? dispatch_semaphore_t semaphore2 = dispatch_semaphore_create(1);

? ? NSMutableArray *array = [[NSMutableArray alloc]init];


? ? for (int i = 0; i<10000; i++) {

? ? ? ? dispatch_async(queue, ^{

? ? ? ? ? ? //等待Dispatch Semaphore的計數值達到大于等于1時,將Dispatch Semaphore的計數值減去1

? ? ? ? ? ? dispatch_semaphore_wait(semaphore2, DISPATCH_TIME_FOREVER);

? ? ? ? ? ? //執行到此時的Dispatch Semaphore的計數值恒為"0",即其它線程處于等待狀態,所以可訪問NSMutableArray類對象的線程只有1個,因此可安全的進行更新

? ? ? ? ? ? [array addObject:[NSNumber numberWithInt:i]];

? ? ? ? ? ? //通過dispatch_semaphore_signal函數將Dispatch Semaphore的計數值加1

? ? ? ? ? ? dispatch_semaphore_signal(semaphore2);

? ? ? ? ? ? //之后,如果有其它通過dispatch_semaphore_wait函數等待Dispatch Semaphore的計數值大于等于1的線程,就由最先等待的線程執行

? ? ? ? });

? ? }



dispatch_once

//dispatch_once函數是保證在應用程序中只執行一次指定處理的API,比如初始化。


? ? //使用是否初始化的標志變量來判斷是否初始化

? ? static? int initialized = NO;

? ? if (initialized == NO) {

? ? ? ? //初始化處理

? ? ? ? initialized = YES;

? ? }

? ? //在大多數情況這種方式下是安全的。但是在多核CPU中,在正在更新表示是否初始化的標志變量時讀取,就有可能多次執行初始化處理


? ? //使用dispatch_once函數

? ? static dispatch_once_t pred;

? ? dispatch_once (&pred,^{

? ? ? ? //初始化處理

? ? });

? ? //dispatch_once函數即使在多線程環境下執行,也可保證百分比安全。



GCDAsyncSocket快速開發Socket通信

參考:http://blog.csdn.net/bianhuanshizhe/article/details/69426022



如有轉載,請說明原處

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
禁止轉載,如需轉載請通過簡信或評論聯系作者。

推薦閱讀更多精彩內容

  • GCD (Grand Central Dispatch) :iOS4 開始引入,使用更加方便,程序員只需要將任務添...
    池鵬程閱讀 1,366評論 0 2
  • iOS多線程之GCD 什么是GCD GCD(grand central dispatch) 是 libdispat...
    comst閱讀 1,224評論 0 0
  • 多線程概念 線程線程指的是:1個CPU執行的CPU命令列為一條無分叉路徑 多線程這種無分叉路徑不止一條,存在多條即...
    我系哆啦閱讀 611評論 0 5
  • 1. GCD簡介 什么是GCD呢?我們先來看看百度百科的解釋簡單了解下概念 引自百度百科:Grand Centra...
    千尋_544f閱讀 411評論 0 0
  • 選擇一個城市,便是選擇了一種生活。 北上廣,都市繁華,人才熙攘,有著無限的機會和可能。無數人帶著激情與熱血,來到這...
    晚風中Sharon閱讀 494評論 7 8