讓線程按順序執行7種方法

来源:https://www.cnblogs.com/wenjunwei/archive/2019/03/25/10573289.html
-Advertisement-
Play Games

摘要:本文使用了7中方法實現在多線程中讓線程按順序運行的方法,主要目的是讓讀者對多線程的使用有更深刻的瞭解。 ...


轉載請註明出處:https://www.cnblogs.com/wenjunwei/p/10573289.html

一.前言

本文使用了7中方法實現在多線程中讓線程按順序運行的方法,涉及到多線程中許多常用的方法,不止為了知道如何讓線程按順序運行,更是讓讀者對多線程的使用有更深刻的瞭解。 使用的方法如下:

  • [1] 使用線程的join方法
  • [2] 使用主線程的join方法
  • [3] 使用線程的wait方法
  • [4] 使用線程的線程池方法
  • [5] 使用線程的Condition(條件變數)方法
  • [6] 使用線程的CountDownLatch(倒計數)方法
  • [7] 使用線程的CyclicBarrier(迴環柵欄)方法
  • [8] 使用線程的Semaphore(信號量)方法

二.實現

我們下麵需要完成這樣一個應用場景:

1.早上;2.測試人員、產品經理、開發人員陸續的來公司上班;3.產品經理規劃新需求;4.開發人員開發新需求功能;5.測試人員測試新功能。

規劃需求,開發需求新功能,測試新功能是一個有順序的,我們把thread1看做產品經理,thread2看做開發人員,thread3看做測試人員。

1.使用線程的join方法

join():是Theard的方法,作用是調用線程需等待該join()線程執行完成後,才能繼續用下運行。

應用場景:當一個線程必須等待另一個線程執行完畢才能執行時可以使用join方法。

package com.wwj.javabase.thread.order;

/**
 * @author wwj
 * 通過子程式join使線程按順序執行
 */
public class ThreadJoinDemo {

    public static void main(String[] args) {
        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("產品經理規劃新需求");
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    thread1.join();
                    System.out.println("開發人員開發新需求功能");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    thread2.join();
                    System.out.println("測試人員測試新功能");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        System.out.println("早上:");
        System.out.println("測試人員來上班了...");
        thread3.start();
        System.out.println("產品經理來上班了...");
        thread1.start();
        System.out.println("開發人員來上班了...");
        thread2.start();
    }
}

運行結果

早上:
測試人員來上班了...
產品經理來上班了...
開發人員來上班了...
產品經理規劃新需求
開發人員開發新需求功能
測試人員測試新功能

2.使用主線程的join方法

這裡是在主線程中使用join()來實現對線程的阻塞。

package com.wwj.javabase.thread.order;

/**
 * @author wwj
 * 通過主程式join使線程按順序執行
 */
public class ThreadMainJoinDemo {

    public static void main(String[] args) throws Exception {

        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("產品經理正在規劃新需求...");
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("開發人員開發新需求功能");
            }
        });

        final Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("測試人員測試新功能");
            }
        });

        System.out.println("早上:");
        System.out.println("產品經理來上班了");
        System.out.println("測試人員來上班了");
        System.out.println("開發人員來上班了");
        thread1.start();
        //在父進程調用子進程的join()方法後,父進程需要等待子進程運行完再繼續運行。
        System.out.println("開發人員和測試人員休息會...");
        thread1.join();
        System.out.println("產品經理新需求規劃完成!");
        thread2.start();
        System.out.println("測試人員休息會...");
        thread2.join();
        thread3.start();
    }
}

運行結果

產品經理來上班了
測試人員來上班了
開發人員來上班了
開發人員和測試人員休息會...
產品經理正在規劃新需求...
產品經理新需求規劃完成!
測試人員休息會...
開發人員開發新需求功能
測試人員測試新功能

3.使用線程的wait方法

wait():是Object的方法,作用是讓當前線程進入等待狀態,同時,wait()也會讓當前線程釋放它所持有的鎖。“直到其他線程調用此對象的 notify() 方法或 notifyAll() 方法”,當前線程被喚醒(進入“就緒狀態”)

notify()和notifyAll():是Object的方法,作用則是喚醒當前對象上的等待線程;notify()是喚醒單個線程,而notifyAll()是喚醒所有的線程。

wait(long timeout):讓當前線程處於“等待(阻塞)狀態”,“直到其他線程調用此對象的notify()方法或 notifyAll() 方法,或者超過指定的時間量”,當前線程被喚醒(進入“就緒狀態”)。

應用場景:Java實現生產者消費者的方式。

package com.wwj.javabase.thread.order;

/**
 * @author wwj
 */
public class ThreadWaitDemo {

    private static Object myLock1 = new Object();
    private static Object myLock2 = new Object();

    /**
     * 為什麼要加這兩個標識狀態?
     * 如果沒有狀態標識,當t1已經運行完了t2才運行,t2在等待t1喚醒導致t2永遠處於等待狀態
     */
    private static Boolean t1Run = false;
    private static Boolean t2Run = false;
    public static void main(String[] args) {

        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (myLock1){
                    System.out.println("產品經理規劃新需求...");
                    t1Run = true;
                    myLock1.notify();
                }
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (myLock1){
                    try {
                        if(!t1Run){
                            System.out.println("開發人員先休息會...");
                            myLock1.wait();
                        }
                        synchronized (myLock2){
                            System.out.println("開發人員開發新需求功能");
                            myLock2.notify();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (myLock2){
                    try {
                        if(!t2Run){
                            System.out.println("測試人員先休息會...");
                            myLock2.wait();
                        }
                        System.out.println("測試人員測試新功能");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        System.out.println("早上:");
        System.out.println("測試人員來上班了...");
        thread3.start();
        System.out.println("產品經理來上班了...");
        thread1.start();
        System.out.println("開發人員來上班了...");
        thread2.start();
    }
}

運行結果:這裡輸出會有很多種順序,主要是因為線程進入的順序,造成鎖住線程的順序不一致。

早上:
測試人員來上班了...
產品經理來上班了...
開發人員來上班了...
測試人員先休息會...
產品經理規劃新需求...
開發人員開發新需求功能
測試人員測試新功能

4.使用線程的線程池方法

JAVA通過Executors提供了四種線程池

  • 單線程化線程池(newSingleThreadExecutor);
  • 可控最大併發數線程池(newFixedThreadPool);
  • 可回收緩存線程池(newCachedThreadPool);
  • 支持定時與周期性任務的線程池(newScheduledThreadPool)。

單線程化線程池(newSingleThreadExecutor):優點,串列執行所有任務。

submit():提交任務。

shutdown():方法用來關閉線程池,拒絕新任務。

應用場景:串列執行所有任務。如果這個唯一的線程因為異常結束,那麼會有一個新的線程來替代它。此線程池保證所有任務的執行順序按照任務的提交順序執行。

package com.wwj.javabase.thread.order;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author wwj
 * 通過SingleThreadExecutor讓線程按順序執行
 */
public class ThreadPoolDemo {

    static ExecutorService executorService = Executors.newSingleThreadExecutor();

    public static void main(String[] args) throws Exception {

        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("產品經理規劃新需求");
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("開發人員開發新需求功能");
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("測試人員測試新功能");
            }
        });

        System.out.println("早上:");
        System.out.println("產品經理來上班了");
        System.out.println("測試人員來上班了");
        System.out.println("開發人員來上班了");
        System.out.println("領導吩咐:");
        System.out.println("首先,產品經理規劃新需求...");
        executorService.submit(thread1);
        System.out.println("然後,開發人員開發新需求功能...");
        executorService.submit(thread2);
        System.out.println("最後,測試人員測試新功能...");
        executorService.submit(thread3);
        executorService.shutdown();
    }
}

運行結果

早上:
產品經理來上班了
測試人員來上班了
開發人員來上班了
領導吩咐:
首先,產品經理規劃新需求...
然後,開發人員開發新需求功能...
最後,測試人員測試新功能...
產品經理規劃新需求
開發人員開發新需求功能
測試人員測試新功能

5.使用線程的Condition(條件變數)方法

Condition(條件變數):通常與一個鎖關聯。需要在多個Contidion中共用一個鎖時,可以傳遞一個Lock/RLock實例給構造方法,否則它將自己生成一個RLock實例。

  • Condition中await()方法類似於Object類中的wait()方法。

  • Condition中await(long time,TimeUnit unit)方法類似於Object類中的wait(long time)方法。

  • Condition中signal()方法類似於Object類中的notify()方法。

  • Condition中signalAll()方法類似於Object類中的notifyAll()方法。

應用場景:Condition是一個多線程間協調通信的工具類,使得某個,或者某些線程一起等待某個條件(Condition),只有當該條件具備( signal 或者 signalAll方法被帶調用)時 ,這些等待線程才會被喚醒,從而重新爭奪鎖。

package com.wwj.javabase.thread.order;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author wwj
 * 使用Condition(條件變數)實現線程按順序運行
 */
public class ThreadConditionDemo {

    private static Lock lock = new ReentrantLock();
    private static Condition condition1 = lock.newCondition();
    private static Condition condition2 = lock.newCondition();

    /**
     * 為什麼要加這兩個標識狀態?
     * 如果沒有狀態標識,當t1已經運行完了t2才運行,t2在等待t1喚醒導致t2永遠處於等待狀態
     */
    private static Boolean t1Run = false;
    private static Boolean t2Run = false;

    public static void main(String[] args) {

        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                System.out.println("產品經理規劃新需求");
                t1Run = true;
                condition1.signal();
                lock.unlock();
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                try {
                    if(!t1Run){
                        System.out.println("開發人員先休息會...");
                        condition1.await();
                    }
                    System.out.println("開發人員開發新需求功能");
                    t2Run = true;
                    condition2.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                lock.unlock();
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                try {
                    if(!t2Run){
                        System.out.println("測試人員先休息會...");
                        condition2.await();
                    }
                    System.out.println("測試人員測試新功能");
                    lock.unlock();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        System.out.println("早上:");
        System.out.println("測試人員來上班了...");
        thread3.start();
        System.out.println("產品經理來上班了...");
        thread1.start();
        System.out.println("開發人員來上班了...");
        thread2.start();
    }
}

運行結果:這裡輸出會有很多種順序,主要是因為線程進入的順序,造成鎖住線程的順序不一致

早上:
測試人員來上班了...
產品經理來上班了...
開發人員來上班了...
測試人員先休息會...
產品經理規劃新需求
開發人員開發新需求功能
測試人員測試新功能

6.使用線程的CountDownLatch(倒計數)方法

CountDownLatch:位於java.util.concurrent包下,利用它可以實現類似計數器的功能。

應用場景:比如有一個任務C,它要等待其他任務A,B執行完畢之後才能執行,此時就可以利用CountDownLatch來實現這種功能了。

package com.wwj.javabase.thread.order;

import java.util.concurrent.CountDownLatch;

/**
 * @author wwj
 * 通過CountDownLatch(倒計數)使線程按順序執行
 */
public class ThreadCountDownLatchDemo {

    /**
     * 用於判斷線程一是否執行,倒計時設置為1,執行後減1
     */
    private static CountDownLatch c1 = new CountDownLatch(1);

    /**
     * 用於判斷線程二是否執行,倒計時設置為1,執行後減1
     */
    private static CountDownLatch c2 = new CountDownLatch(1);

    public static void main(String[] args) {
        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("產品經理規劃新需求");
                //對c1倒計時-1
                c1.countDown();
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //等待c1倒計時,計時為0則往下運行
                    c1.await();
                    System.out.println("開發人員開發新需求功能");
                    //對c2倒計時-1
                    c2.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //等待c2倒計時,計時為0則往下運行
                    c2.await();
                    System.out.println("測試人員測試新功能");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        System.out.println("早上:");
        System.out.println("測試人員來上班了...");
        thread3.start();
        System.out.println("產品經理來上班了...");
        thread1.start();
        System.out.println("開發人員來上班了...");
        thread2.start();
    }
}

運行結果

早上:
測試人員來上班了...
產品經理來上班了...
開發人員來上班了...
產品經理規劃新需求
開發人員開發新需求功能
測試人員測試新功能

7.使用CyclicBarrier(迴環柵欄)實現線程按順序運行

CyclicBarrier(迴環柵欄):通過它可以實現讓一組線程等待至某個狀態之後再全部同時執行。叫做迴環是因為當所有等待線程都被釋放以後,CyclicBarrier可以被重用。我們暫且把這個狀態就叫做barrier,當調用await()方法之後,線程就處於barrier了。

應用場景:公司組織春游,等待所有的員工到達集合地點才能出發,每個人到達後進入barrier狀態。都到達後,喚起大家一起出發去旅行。

package com.wwj.javabase.thread.order;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * @author wwj
 * 使用CyclicBarrier(迴環柵欄)實現線程按順序運行
 */
public class CyclicBarrierDemo {

    static CyclicBarrier barrier1 = new CyclicBarrier(2);
    static CyclicBarrier barrier2 = new CyclicBarrier(2);

    public static void main(String[] args) {

        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("產品經理規劃新需求");
                    //放開柵欄1
                    barrier1.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //放開柵欄1
                    barrier1.await();
                    System.out.println("開發人員開發新需求功能");
                    //放開柵欄2
                    barrier2.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        });

        final Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //放開柵欄2
                    barrier2.await();
                    System.out.println("測試人員測試新功能");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        });

        System.out.println("早上:");
        System.out.println("測試人員來上班了...");
        thread3.start();
        System.out.println("產品經理來上班了...");
        thread1.start();
        System.out.println("開發人員來上班了...");
        thread2.start();
    }
}

運行結果

早上:
測試人員來上班了...
產品經理來上班了...
開發人員來上班了...
產品經理規劃新需求
開發人員開發新需求功能
測試人員測試新功能

8.使用Sephmore(信號量)實現線程按順序運行

Sephmore(信號量):Semaphore是一個計數信號量,從概念上將,Semaphore包含一組許可證,如果有需要的話,每個acquire()方法都會阻塞,直到獲取一個可用的許可證,每個release()方法都會釋放持有許可證的線程,並且歸還Semaphore一個可用的許可證。然而,實際上並沒有真實的許可證對象供線程使用,Semaphore只是對可用的數量進行管理維護。

acquire():當前線程嘗試去阻塞的獲取1個許可證,此過程是阻塞的,當前線程獲取了1個可用的許可證,則會停止等待,繼續執行。

release():當前線程釋放1個可用的許可證。

應用場景:Semaphore可以用來做流量分流,特別是對公共資源有限的場景,比如資料庫連接。假設有這個的需求,讀取幾萬個文件的數據到資料庫中,由於文件讀取是IO密集型任務,可以啟動幾十個線程併發讀取,但是資料庫連接數只有10個,這時就必須控制最多只有10個線程能夠拿到資料庫連接進行操作。這個時候,就可以使用Semaphore做流量控制。

package com.wwj.javabase.thread.order;

import java.util.concurrent.Semaphore;
/**
 * @author wwj
 * 使用Sephmore(信號量)實現線程按順序運行
 */
public class SemaphoreDemo {
    private static Semaphore semaphore1 = new Semaphore(1);
    private static Semaphore semaphore2 = new Semaphore(1);
    public static void main(String[] args) {
        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("產品經理規劃新需求");
                semaphore1.release();
            }
        });

        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    semaphore1.acquire();
                    System.out.println("開發人員開發新需求功能");
                    semaphore2.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    semaphore2.acquire();
                    thread2.join();
                    semaphore2.release();
                    System.out.println("測試人員測試新功能");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        System.out.println("早上:");
        System.out.println("測試人員來上班了...");
        thread3.start();
        System.out.println("產品經理來上班了...");
        thread1.start();
        System.out.println("開發人員來上班了...");
        thread2.start();
    }
}

運行結果

早上:
測試人員來上班了...
產品經理來上班了...
開發人員來上班了...
產品經理規劃新需求
開發人員開發新需求功能
測試人員測試新功能

總結

看完了這麼多種方法,是不是對多線程有了更深入的瞭解呢?不妨自己試試吧(代碼拷貝均可運行)

使用的場景還有很多,根據開發需求場景,選擇合適的方法,達到事半功倍的效果。

 

本文歡迎各位轉載,但是轉載文章之後必須在文章開頭給出原文鏈接。感謝您的閱讀,如果您覺得閱讀本文對您有幫助,請點個“推薦”支持一下。

 


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

-Advertisement-
Play Games
更多相關文章
  • 作為前端開發人員的必修課,CSS3翻轉能帶我們完成許多基本動效,本期我們將用CSS3實現hover翻轉效果~ 第一步非常簡單,我們簡單畫1個演示方塊,為其 添加transition和transform屬性 : ? 1 2 3 4 5 6 7 8 9 10 11 // 本示例均使用Sass語法 .bl ...
  • jQuery中創建元素及追加元素 DOM中可以動態創建元素:document.createElement(“標簽的名字”); jQuery中同樣可以創建元素標簽,並且返回的就是jQuery對象,可以直接調用方法進行使用 1.append 方法用來在元素的末尾追加元素(最後一個子節點)。增加元素末尾 ...
  • 大家好~又見面了。 今天呢我想給大家,也給我自己, 對我的個人網站yanyy.cn/yanyy 做一個全面的整理和分析。 也給有這方面想法的朋友一個參考。 做網站的有愛好也有帶有目的性的。 不過我還是希望大家能夠傳播正能量~ 進入正題 網站首頁 看到上圖,是網站的首頁。 首頁上用戶直接看得到的部分有 ...
  • 圖片校驗碼原理就是圖片是後端生成的前端只是前後端傳過來的數據流做些處理展示即可,先直接上核心代碼圖: 這裡就是簡單得對axios的一些預設項屬性重寫:最後你只需要將resolve的內容插入頁面的<img src="resolve(data:img/png;base64,&{base64})">即可, ...
  • 把常見的日期格式如:YYYY-MM-DD 轉換成一種更易讀的格式。 易讀格式應該是用月份名稱代替月份數字,用序數詞代替數字來表示天 (1st 代替 1). 記住不要顯示那些可以被推測出來的信息: 如果一個日期區間里結束日期與開始日期相差小於一年,則結束日期就不用寫年份了;在這種情況下,如果月份開始和 ...
  • 一、基於度量對程式結構的分析 1. 第一次作業 1.1 基於類的分析的度量 首先,基於類的屬性個數,方法個數,每個方法的規模,每個方法的控制分支數目,類總代碼規模等特征對本次作業的結構進行分析。 1.2 基於類間內聚和耦合的度量 我使用了MetricsReloaded插件來對代碼的複雜度進行了分析。 ...
  • Sentry(直譯為:哨兵)是一個開源錯誤跟蹤服務,幫助開發人員實時監控和修複崩潰 Sentry本質上是一種幫助您實時監控和修複崩潰的服務 1. 安裝客戶端SDK 這裡我們安裝Java平臺的SDK,而且使用logback的方式集成 https://docs.sentry.io/clients/jav ...
  • 前言 開心一刻 那年去相親,地點在飯店裡,威特先上了兩杯水,男方紳士的喝了一口,咧嘴咋舌輕放桌面,手撫額頭閉眼一臉陶醉,白水硬是喝出了82年拉菲的感覺。如此有生活情調的幽默男人,果斷拿下,相處後卻發現他比較木訥,問他為什麼那天喝水那麼有趣,他仰頭道:鬼知道那杯水怎麼那麼燙啊! 是什麼 Factory ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...