iOS多線程開發之GCD(中篇)

来源:http://www.cnblogs.com/beckwang0912/archive/2017/07/07/7112121.html
-Advertisement-
Play Games

前文回顧: 上篇博客講到GCD的實現是由隊列和任務兩部分組成,其中獲取隊列的方式有兩種,第一種是通過GCD的API的dispatch_queue_create函數生成Dispatch Queue;第二種是直接使用系統提供的標準Dispatch Queue :Main Dispatch Queue和G ...


前文回顧:

      上篇博客講到GCD的實現是由隊列和任務兩部分組成,其中獲取隊列的方式有兩種,第一種是通過GCD的API的dispatch_queue_create函數生成Dispatch Queue;第二種是直接使用系統提供的標準Dispatch Queue :Main Dispatch Queue和Global Dispatch Queue,具體的實現方式請參照上篇博客《iOS多線程開發之GCD(上篇)》。

 

這篇博客主要講解以下蘋果提供的一些常用GCD和代碼示例及其註意點。

  • dispatch_set_target_queue
  • dispatch_after
  • dispatch_once / dispatch_apply
  • Dispatch Group
  • dispatch_barrier_sync
  • dispatch_suspend / dispatch_resume
  • Dispatch Semaphore

一、dispatch_set_target_queue 

        dispatch_set_target_queue中涉及的代碼示例來源於網路

        1、dispatch_set_target_queue 更改Dispatch Queue的執行優先順序

        dispatch_queue_create函數生成的DisPatch Queue不管是Serial DisPatch Queue還是Concurrent Dispatch Queue,執行的優先順序都與預設優先順序的Global Dispatch queue相同,如果需要變更生成的Dispatch Queue的執行優先順序則需要使用dispatch_set_target_queue函數。

dispatch_queue_t serialQueue = dispatch_queue_create("com.beckwang.www",NULL);

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0);
 
// 第一個參數為要變更優先順序的queue,第二個參數是參照物,既將第一個queue的優先順序和第二個queue的優先順序設置一樣。
dispatch_set_target_queue(serialQueue, globalQueue);

       2、dispatch_set_target_queue作為執行階層,修改隊列的目標隊列使多個serial queue在目標queue上一次只有一個執行

              第一種情況:使用dispatch_set_target_queue(Dispatch Queue1, Dispatch Queue2)實現隊列的動態調度管理
- (void)testTargetQueue2 {
    //創建一個串列隊列queue1
    dispatch_queue_t queue1 = dispatch_queue_create("test.1", DISPATCH_QUEUE_SERIAL);
    //創建一個串列隊列queue2
    dispatch_queue_t queue2 = dispatch_queue_create("test.2", DISPATCH_QUEUE_SERIAL);
    
    //使用dispatch_set_target_queue()實現隊列的動態調度管理
    dispatch_set_target_queue(queue1, queue2);
    
    /*
     那麼dispatchA上還未運行的block會在dispatchB上運行。這時如果暫停dispatchA運行:
     dispatch_suspend(dispatchA);
     這時則只會暫停dispatchA上原來的block的執行,dispatchB的block則不受影響。而如果暫停dispatchB的運行,則會暫停dispatchA的運行。
     dispatch隊列不支持cancel(取消),沒有實現dispatch_cancel()函數,不像NSOperationQueue,不得不說這是個小小的缺憾
     */

    dispatch_async(queue1, ^{
        for (NSInteger i = 0; i < 10; i++) {
            NSLog(@"queue1:%@, %ld", [NSThread currentThread], i);
            [NSThread sleepForTimeInterval:0.5];
            if (i == 5) {
                dispatch_suspend(queue2);
            }
        }
    });
    
    dispatch_async(queue1, ^{
        for (NSInteger i = 0; i < 100; i++) {
            NSLog(@"queue1:%@, %ld", [NSThread currentThread], i);
        }
        
    });
    
    dispatch_async(queue2, ^{
        for (NSInteger i = 0; i < 100; i++) {
            NSLog(@"queue2:%@, %ld", [NSThread currentThread], i);
        }
    });
}
View Code       第二種情況:使用dispatch_set_target_queue將多個串列的queue指定到了同一目標,那麼著多個串列queue在目標queue上就是一次只能執行一個(化並行為串列)。

        適用場景:一般都是把一個任務放到一個串列的queue中,如果這個任務被拆分了,被放置到多個串列的queue中,但實際還是需要這個任務同步執行,那麼就會有問題,因為多個串列queue之間是並行的。這時候dispatch_set_target_queue將起到作用。

      (1)沒有使用dispatch_set_target_queue時:

- (void)testTargetQueue3 {
    //1.創建目標隊列
    //dispatch_queue_t targetQueue = dispatch_queue_create("test.target.queue", DISPATCH_QUEUE_SERIAL);
    
    //2.創建3個串列隊列
    dispatch_queue_t queue1 = dispatch_queue_create("test.1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue2 = dispatch_queue_create("test.2", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue3 = dispatch_queue_create("test.3", DISPATCH_QUEUE_SERIAL);
    
    //3.將3個串列隊列分別添加到目標隊列
    //dispatch_set_target_queue(queue1, targetQueue);
    //dispatch_set_target_queue(queue2, targetQueue);
    //dispatch_set_target_queue(queue3, targetQueue);
    
    dispatch_async(queue1, ^{
        NSLog(@"1 in");
        [NSThread sleepForTimeInterval:3.f];
        NSLog(@"1 out");
    });
    
    dispatch_async(queue2, ^{
        NSLog(@"2 in");
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"2 out");
    });
    dispatch_async(queue3, ^{
        NSLog(@"3 in");
        [NSThread sleepForTimeInterval:1.f];
        NSLog(@"3 out");
    });
}

      列印結果:

2017-07-04 19:52:51.915 Test[5759:927698] 1 in
2017-07-04 19:52:51.915 Test[5759:927699] 2 in
2017-07-04 19:52:51.915 Test[5759:927701] 3 in
2017-07-04 19:52:52.916 Test[5759:927701] 3 out
2017-07-04 19:52:53.921 Test[5759:927699] 2 out
2017-07-04 19:52:54.919 Test[5759:927698] 1 out 

     結論:多個串列queue之間是並行的!

   (2)使用dispatch_set_target_queue設置target

- (void)testTargetQueue3 {
    //1.創建目標隊列
    dispatch_queue_t targetQueue = dispatch_queue_create("test.target.queue", DISPATCH_QUEUE_SERIAL);
    
    //2.創建3個串列隊列
    dispatch_queue_t queue1 = dispatch_queue_create("test.1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue2 = dispatch_queue_create("test.2", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue3 = dispatch_queue_create("test.3", DISPATCH_QUEUE_SERIAL);
    
    //3.將3個串列隊列分別添加到目標隊列
    dispatch_set_target_queue(queue1, targetQueue);
    dispatch_set_target_queue(queue2, targetQueue);
    dispatch_set_target_queue(queue3, targetQueue);
    
    
    dispatch_async(queue1, ^{
        NSLog(@"1 in");
        [NSThread sleepForTimeInterval:3.f];
        NSLog(@"1 out");
    });
    
    dispatch_async(queue2, ^{
        NSLog(@"2 in");
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"2 out");
    });
    dispatch_async(queue3, ^{
        NSLog(@"3 in");
        [NSThread sleepForTimeInterval:1.f];
        NSLog(@"3 out");
    });
}

      列印結果:

2017-07-04 19:58:33.667 Test[5830:968024] 1 in
2017-07-04 19:58:36.672 Test[5830:968024] 1 out
2017-07-04 19:58:36.672 Test[5830:968024] 2 in
2017-07-04 19:58:38.678 Test[5830:968024] 2 out
2017-07-04 19:58:38.679 Test[5830:968024] 3 in
2017-07-04 19:58:39.683 Test[5830:968024] 3 out

     結論:多個串列queue之間是串列的!

 

二、dispatch_after

     如果需要延時處理某件事情,我們可以使用dispatch_after,需要註意的是dispatch_after並不是將任務追加到隊列dispatch_queue後再根據時間參數延遲執行block代碼,而是在指定時間後追加任務到到dispatch_queue。代碼示例:

dispatch_time_t time=dispatch_time(DISPATCH_TIME_NOW, 3*NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
        NSLog(@"這裡是dispatch_after測試");
});

//
dispatch_get_main_queue ---> diapatch_get_gloab_queue 就可以更改執行線程


     實現延時處理除了上面的GCD(dispatch_after)外,還可以通過以下方法:

     (1)performSelector(NSObject)方法:

// 不帶參數
[self performSelector:@selector(doSomething) withObject:self afterDelay:3.0f];

// 帶參數
[self performSelector:@selector(delayDo:) withObject:@"paramtest" afterDelay:3.0f];

// 取消全部
[NSObject cancelPreviousPerformRequestsWithTarget:self];

// 取消不傳參的方法
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(delayDo:) object:nil];

// 取消傳參的方法
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(delayDo:) object:@"paramtest"];

    (2)NSTimer的類方法:

// 不帶參數
[NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(doSomething) userInfo:nil repeats:NO];

// 帶參數
[NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(doSomething) userInfo:@"paramtest" repeats:NO];

       使用NSTimer時註意事項可以參考我的另外一篇博客《實現定時器NSTimer的高逼格方法

     (3)sleep(NSThreed)

[NSThread sleepForTimeInterval:1.0f];
// 這裡執行延遲處理代碼
[self doSomething];

 

三、dispatch_once 和 dispacth_apply

     dispatch_once整個app運行周期內只執行一次代碼,多用於單例模式。

dispatch_once_t *predicate:一個全局的變數   dispatch_block_t block:block函數塊     

     dispatch_apply讓指定代碼按設定次數多次執行,dispatch_apply類似一個for迴圈,會在指定的dispatch queue中運行block任務n次,如果隊列是併發隊列,則會併發執行block任務,如果隊列是串列隊列,則block任務只會同步執行,但是dispatch_apply是一個同步調用,block任務執行n次後才返回。

size_t iterations:執行次數  dispatch_queue_t queue:隊列   void (^block)(size_t):block函數塊

   代碼示例:

 (1)dispatch_once 

   自定義block函數塊

//定義block
typedef void (^BLOCK)(void);
    
//將執行代碼封裝到block中
BLOCK myBlock = ^(){
    static int count = 0;
    NSLog(@"count=%d",count++);
};

  執行

// 只會執行一次
static dispatch_once_t predicate;
dispatch_once(&predicate, myBlock);

 列印結果:count  = 0;

 

  (2) dispatch_apply

  自定義block

//定義block
typedef void (^BLOCK)(size_t);
    
//將函數封裝到block
BLOCK myBlock = ^(size_t size){
    static int count = 0;
    NSLog(@"count=%d",count++);      
};

  執行

dispatch_apply(5, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), myBlock);

  列印結果:

count = 0
count = 2
count = 3
count = 1
count = 4

    顯而易見,如果dispatch_apply的隊列是自定義的串列隊列

dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);

    輸出結果將是:

count = 0
count = 1
count = 2
count = 3
count = 4

   dispatch_apply 可以處理一個任務重覆執行次數量級比較大的應用場景,假設從伺服器上獲取一組數組數據(超過100個元素對象)然後進行字典轉化模型

   多線程併發處理:

// 多線程併發處理,可能造成線程爆炸及死鎖
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);

for (int i = 0; i < 999; i++){
   dispatch_async(queue, ^{
         // 字典轉模型 
   });
}
dispatch_barrier_sync(dispatch_get_main_queue(), ^{
       NSLog(@"主線程更新");
});

---------------------------這裡是分割線---------------------------

// dispatch_apply 方式 (優先選擇)
NSArray *dictArray = nil;

dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
   
 dispatch_async(queue, ^{

        dispatch_apply(dictArray.count, queue,  ^(size_t index){
            //字典轉模型

        });

        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"主線程更新");
        });
    });

 

四、Dispatch Group

         在追加到Dispatch Queue中的多個任務處理全部完畢之後想執行結束處理。如果只是使用一個Serial Dispatch Queue(串列隊列)時,只要將想執行的處理全部追加到該串列隊列中併在最後追加結束處理即可,但是在使用Concurrent Queue 時,可能會同時使用多個Dispatch Queue時,這就需要使用Dispatch Group。

- (void)testDispatchGroup{
    
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("com.gcdgroup.www", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_group_async(group, queue, ^{
        for (int i = 0; i < 10; i++) {
            if (i == 9) {
                NSLog(@"test001");
            }
        }
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"test002");
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"test003");
    });
    
    dispatch_group_notify(group, queue, ^{
        NSLog(@"全部完成");
    });
}

     列印結果:

2017-07-06 23:30:57.449 Test[8724:1743565] test002
2017-07-06 23:30:57.449 Test[8724:1743547] test003
2017-07-06 23:30:57.449 Test[8724:1743549] test001
2017-07-06 23:30:57.449 Test[8724:1743547] 全部完成

    Dispatch Group廣泛運用到非同步獲取網路數據最後彙總的情況,如非同步獲取多張網路圖片資源後拼接成一張圖片等等。

 

五、dispatch_barrier_async

    在訪問資料庫和文件時,如前所述,使用Serial Dispatch Queue可避免數據資源的競爭問題。眾所周知,寫處理與寫處理,寫處理與讀處理會發生數據一致性或數據競爭問題,但是讀處理與讀處理之前不存在數據一致性問題,為了提高效率我們可以這樣設想:讀處理可以追加到Concurrent Dispatch Queue(併發隊列)中,而寫處理在任意一個沒有讀取處理執行的狀態下追加到Serial Dispatch Queue(串列隊列)中(在寫處理結束之前,讀處理不可執行)。

代碼示例:

- (void)testDispatchBarrier{
    
    dispatch_queue_t queue = dispatch_queue_create("com.gcdbarrier.www", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        
        NSLog(@"block001_read");
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"block002_read");
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"block003_read");
        
    });
    
    dispatch_barrier_sync(queue, ^{
        
        NSLog(@"block004_write");
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"block005_read");
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"block006_read");
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"block007_read");
    });
    
}

   列印結果:

2017-07-06 23:59:27.936 Test[9080:1907162] block003_read
2017-07-06 23:59:27.936 Test[9080:1907194] block002_read
2017-07-06 23:59:27.936 Test[9080:1907163] block001_read
2017-07-06 23:59:27.937 Test[9080:1907028] block004_write
2017-07-06 23:59:27.937 Test[9080:1907163] block005_read
2017-07-06 23:59:27.937 Test[9080:1907162] block007_read
2017-07-06 23:59:27.937 Test[9080:1907194] block006_read

 

 

六、dispatch_suspend / dispatch_resume

      dispatch_suspend,dispatch_resume提供了“掛起、恢復”隊列的功能,簡單來說,就是可以暫停、恢復隊列上的任務。但是這裡的“掛起”,並不能保證可以立即停止隊列上正在運行的block,而是在當前block執行完成後,暫停後續的block執行。

// 掛起指定隊列
dispatch_suspend(queue);
// 恢復指定隊列
dispatch_resume(queue); 

      代碼示例:

- (void)gcdSuspendResume{

    dispatch_queue_t queue = dispatch_queue_create("com.test.gcd", DISPATCH_QUEUE_SERIAL);
    // 提交第一個block,延時5秒列印。
    dispatch_async(queue, ^{
        sleep(5);
        NSLog(@"After 5 seconds...");
    });
    // 提交第二個block,也是延時5秒列印
    dispatch_async(queue, ^{
        sleep(5);
        NSLog(@"After 5 seconds again...");
    });
    // 延時一秒
    NSLog(@"sleep 1 second...");
    sleep(1);
    // 掛起隊列
    NSLog(@"suspend...");
    dispatch_suspend(queue);
    // 延時10秒
    NSLog(@"sleep 10 second...");
    sleep(10);
    // 恢復隊列
    NSLog(@"resume...");
    dispatch_resume(queue);
}

     列印結果:

2017-07-07 14:29:44.329 beck.wang[1045:77001] sleep 1 second...
2017-07-07 14:29:45.330 beck.wang[1045:77001] suspend...
2017-07-07 14:29:45.330 beck.wang[1045:77001] sleep 10 second...
2017-07-07 14:29:49.333 beck.wang[1045:77045] After 5 seconds...
2017-07-07 14:29:55.331 beck.wang[1045:77001] resume...
2017-07-07 14:30:00.336 beck.wang[1045:77045] After 5 seconds again...

 

七、Dispatch Semaphore 

       dispatch_semaphore(信號量)是基於計數器的一種多線程同步機制,是GCD控制併發的一種方式。

       信號量是一個整形值並且具有一個初始計數值,並且支持兩個操作:信號通知和等待。當一個信號量被信號通知,其計數會被增加。當一個線程在一個信號量上等待時,線程會被阻塞(如果有必要的話),直至計數器大於零,然後線程會減少這個計數。

      在GCD中有三個函數是semaphore的操作,分別是:dispatch_semaphore_create、dispatch_semaphore_signal、dispatch_semaphore_wait。

      1、dispatch_semaphore_create  創建具有初始值的信號量

// 輸出一個dispatch_semaphore_t類型且值為value的信號量。這裡的傳入的參數value必須>=0,否則dispatch_semaphore_create會返回NULL。
dispatch_semaphore_t 
dispatch_semaphore_create(long value);

// 示例
dispatch_semaphore_t  semaphore = dispatch_semaphore_create(1);

 

     2、dispatch_semaphore_signal  發送信號量,讓信號量總數+1

long dispatch_semaphore_signal(dispatch_semaphore_tdsema) 

     3、dispatch_semaphore_wait    等待信號量,當信號總量< 0 的時候等待設置的timeout參數,否則就可以正常的執行,並讓信號總量 -1

long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);

 

     註意:timeout是dispatch_time_t類型,不可直接使用其他類型(int或者float等)。如果等待的期間desema的值被dispatch_semaphore_signal函數加1了,且dispatch_semaphore_wait所處線程獲得了信號量,那麼就繼續向下執行並將信號量減1;如果等待期間沒有獲取到信號量或者信號量的值一直為0,那麼等到timeout時,其所處線程自動執行其後語句。

    蘋果給了兩個timeout的巨集定義,也是比較常用的。

DISPATCH_TIME_NOW  //當前時間
DISPATCH_TIME_FOREVER // 一直等待

 

   如果需要自定義timeout可以使用

dispatch_time_t
dispatch_time(dispatch_time_t when, int64_t delta);

dispatch_time_t
dispatch_walltime(const struct timespec *_Nullable when, int64_t delta);

 

喜歡我的文章請點擊關註哦,我將在以後的工作中爭取寫出更高質量的博客,交流分享!……^_^


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一個經典的問題: 0.1+0.2==0.3 答案是:false 因為:0.1+0.2=0.30000000000000004 第一次看到這個結果就是無比驚訝,下巴碰到地上,得深入瞭解下問題出在哪裡,該怎麼去調整。 產生問題的原因 在JS中數值類型就只有number類型,沒有int,float,dou ...
  • 好幾天沒有進行每日一練了,除了工作原因之外,還有因為近期看到同事發的各種剛入職場的學弟學妹們的簡歷,被上面寫的各種掌握的技能所刺激了。雖然可能不是那麼盡實,但著實的push自己一把,決定先把canvas啃下來。 我試水了畫了一個時鐘,和MDN的例子略有一點不同。I work it by myself ...
  • 最近在百度搜索的時候,當你輸入一個字或者詞的時候,他會給你們彈出一個下拉框出來,裡面是和你相關的搜索提示 比如 我輸入楊字,他會給我提示以下搜索提示 我嘗試著用JavaScript做了一個類似的練習,以下是我用VS2013寫的代碼,有不對的地方,請不吝賜教。 效果展示: 關於這個練習我有以下幾點思索 ...
  • WebSocket是HTML5開始提供的一種單個TCP連接上進行全雙工通訊的協議。在WebSocket API中,瀏覽器和伺服器只需要做一個握手的動作,然後,瀏覽器和伺服器之間就形成了一條快速通道。兩者之間就直接可以數據相互傳送。瀏覽器通過JavaScript向伺服器發出建立WebSocket連接的 ...
  • 重新看js閉包的時候看到了《大部分人都會做錯的經典JS閉包面試題》,自己理解並記錄了下想法。很多部分博主已經講得很詳細了,只是後面的解釋部分文字有點繞。 原帖地址:http://web.jobbole.com/84328/ 先貼代碼 問:每一個輸出分別是什麼? 答案: 來逐步拆解: ...
  • 前言: 項目開發中遇到了需要將HTML頁面的內容導出為一個word文檔,所以有了這邊隨筆。 當然,項目開發又時間有點緊迫,第一時間想到的是用插件,所以百度了下。下麵就介紹兩個導出word文檔的方法。 法一:通過jquery.wordexport.js導出word 備註:相容IE9以上 大概瀏覽了下j ...
  • Xcode導出App一般問題及其解決方法 問題一:開發者協議變更問題。 變更後打包會出現如下圖A警告,此時點擊 ”visit developer website“進入Apple開發者網站,登錄開發者賬號後會出現如圖B後,點擊“Review Agreement” 進入下一步確認同意條款即可。 (圖A) ...
  • Android精選源碼 Android優質博客 Kotlin編程中使用Glide v4 Generated API前期準備:在Gralde中引用Glide庫:dependencies {compile fileTree(include: , dir: 'libs')androidTestCompil ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...