.NET Core 多線程的用法,以及用例

来源:https://www.cnblogs.com/goodluckily/archive/2023/03/21/17239215.html
-Advertisement-
Play Games

1.使用 Thread 類 Thread 類是 .NET 中最基本的多線程操作方式之一,可以使用它創建並啟動新線程。以下是一個簡單的例子,創建一個新的線程並運行: using System; using System.Threading; class Program { static void Ma ...


1.使用 Thread 類
Thread 類是 .NET 中最基本的多線程操作方式之一,可以使用它創建並啟動新線程。以下是一個簡單的例子,創建一個新的線程並運行:

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        Thread t = new Thread(new ThreadStart(ThreadProc));
        t.Start();
        
        // 等待線程執行結束
        t.Join();
        
        Console.WriteLine("Main thread exiting.");
    }
    
    static void ThreadProc()
    {
        Console.WriteLine("ThreadProc starting...");
        Thread.Sleep(1000);
        Console.WriteLine("ThreadProc ending.");
    }
}

2.使用 Task 類
Task 類是 .NET 中推薦使用的多線程操作方式之一,它可以更方便地管理非同步操作和多個任務。以下是一個簡單的例子,創建一個新的 Task 並啟動:

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        await Task.Run(() =>
        {
            Console.WriteLine("Task starting...");
            Thread.Sleep(1000);
            Console.WriteLine("Task ending.");
        });

        Console.WriteLine("Main thread exiting.");
    }
}

3.使用 Parallel 類
Parallel 類可以讓我們更方便地進行並行化操作,它提供了一系列方法,可以將一個任務分割成多個小任務,並讓多個線程同時執行這些小任務。以下是一個簡單的例子:

using System;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        Parallel.For(0, 10, i =>
        {
            Console.WriteLine("Task {0} starting...", i);
            Thread.Sleep(1000);
            Console.WriteLine("Task {0} ending.", i);
        });

        Console.WriteLine("Main thread exiting.");
    }
}

4.使用 async/await
在 .NET Core 中,可以使用 async/await 關鍵字進行非同步操作,這是一種非常方便的操作多線程的方式。async/await 讓代碼看起來像是同步的,但實際上是在後臺使用多線程非同步執行的。以下是一個簡單的例子:

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        Console.WriteLine("Main thread starting...");

        await Task.Run(() =>
        {
            Console.WriteLine("Task starting...");
            Thread.Sleep(1000);
            Console.WriteLine("Task ending.");
        });

        Console.WriteLine("Main thread exiting.");
    }
}

5.使用 Concurrent 類
Concurrent 類提供了線程安全的集合和隊列,它們可以在多個線程中同時訪問和修改,而不會發生衝突和數據損壞。以下是一個簡單的例子,使用 ConcurrentQueue 存儲數據:

using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        ConcurrentQueue<int> queue = new ConcurrentQueue<int>();

        // 並行化生產數據
        Parallel.For(0, 10, i =>
        {
            Console.WriteLine("Task {0} producing...", i);
            queue.Enqueue(i);
        });

        // 並行化消費數據
        Parallel.For(0, 10, i =>
        {
            int value;
            if (queue.TryDequeue(out value))
            {
                Console.WriteLine("Task {0} consuming {1}...", i, value);
            }
            else
            {
                Console.WriteLine("Task {0} found queue empty...", i);
            }
        });

        Console.WriteLine("Main thread exiting.");
    }
}

以下是一些高級和複雜一些的操作多線程的用法和技巧:

1.使用 Lock 和 Monitor
在多線程中,如果多個線程同時訪問和修改共用資源,會導致數據損壞和程式崩潰。為了避免這種情況,可以使用 lock 和 Monitor 關鍵字進行同步。lock 和 Monitor 用於獲取對象的鎖,並保證在同一時間只有一個線程可以訪問該對象。以下是一個簡單的例子:

using System;
using System.Threading;

class Program
{
    static object _lock = new object();
    static int _counter = 0;

    static void Main()
    {
        Thread t1 = new Thread(IncrementCounter);
        Thread t2 = new Thread(IncrementCounter);

        t1.Start();
        t2.Start();

        t1.Join();
        t2.Join();

        Console.WriteLine("Counter = {0}", _counter);
    }

    static void IncrementCounter()
    {
        for (int i = 0; i < 100000; i++)
        {
            lock (_lock)
            {
                _counter++;
            }
        }
    }
}

2.使用 CancellationToken 和 TaskCompletionSource
在非同步操作中,有時需要取消任務或等待任務完成後執行其他操作。為了實現這些功能,可以使用 CancellationToken 和 TaskCompletionSource 類。CancellationToken 用於取消任務,TaskCompletionSource 用於等待任務完成並返回結果。以下是一個簡單的例子:

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        using (CancellationTokenSource cts = new CancellationTokenSource())
        {
            Task<int> task = DoWorkAsync(cts.Token);

            // 等待任務完成或取消
            Task completedTask = await Task.WhenAny(task, Task.Delay(5000));
            if (completedTask == task)
            {
                Console.WriteLine("Result = {0}", await task);
            }
            else
            {
                Console.WriteLine("Task cancelled.");
                cts.Cancel();
            }
        }
    }

    static async Task<int> DoWorkAsync(CancellationToken cancellationToken)
    {
        try
        {
            await Task.Delay(2000, cancellationToken);
            return 42;
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("DoWorkAsync cancelled.");
            throw;
        }
    }
}

3.使用 ThreadLocal 和 ExecutionContext
在某些情況下,需要在多個線程中共用變數,並且每個線程需要使用不同的值。為了實現這個目標,可以使用 ThreadLocal 類。ThreadLocal 類為每個線程提供一個獨立的變數副本,使得每個線程都可以使用不同的值。以下是一個簡單的例子:

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static ThreadLocal<int> _counter = new ThreadLocal<int>(() => 0);

    static void Main()
    {
        Parallel.For(0, 10, i =>
        {
            _counter.Value++;
            Console.WriteLine("Thread {0} counter = {1}", Thread.CurrentThread.ManagedThreadId, _counter.Value);
        });

        Console.WriteLine("Main thread exiting.");
    }
}
ExecutionContext 類可以用於在多個線程中共用數據

4.使用 Parallel 類和 PLINQ
Parallel 類和 PLINQ(Parallel LINQ)是 .NET Framework 中用於並行處理數據的工具。Parallel 類提供了一些方法,如 For 和 ForEach,可以輕鬆地將迴圈並行化。PLINQ 則是 LINQ 的並行版本,它可以將查詢操作並行化。以下是一個簡單的例子:

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        int[] numbers = Enumerable.Range(0, 1000000).ToArray();

        // 並行迴圈
        Parallel.ForEach(numbers, number =>
        {
            Console.WriteLine("Thread {0} processing number {1}.", Thread.CurrentThread.ManagedThreadId, number);
        });

        // 並行查詢
        var result = numbers.AsParallel().Where(number => number % 2 == 0).Sum();
        Console.WriteLine("Result = {0}", result);
    }
}

5.使用 SemaphoreSlim 和 CountdownEvent
SemaphoreSlim 和 CountdownEvent 是用於控制多個線程之間的同步和協作的類。SemaphoreSlim 可以用於限制同時訪問某些資源的線程數量,CountdownEvent 可以用於在所有線程完成某些操作後恢復執行。以下是一個簡單的例子:

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static SemaphoreSlim _semaphore = new SemaphoreSlim(2);
    static CountdownEvent _countdown = new CountdownEvent(2);

    static void Main()
    {
        Task t1 = Task.Run(() => DoWork(1));
        Task t2 = Task.Run(() => DoWork(2));
        Task t3 = Task.Run(() => DoWork(3));
        Task t4 = Task.Run(() => DoWork(4));

        Task.WaitAll(t1, t2, t3, t4);

        Console.WriteLine("Main thread exiting.");
    }

    static void DoWork(int id)
    {
        _semaphore.Wait();

        try
        {
            Console.WriteLine("Thread {0} working.", id);
            Thread.Sleep(2000);
        }
        finally
        {
            _semaphore.Release();
            _countdown.Signal();
        }
    }
}

在這個例子中,SemaphoreSlim 限制了同時執行的線程數量,CountdownEvent 則用於在所有線程完成後恢復執行

6.使用 TaskCompletionSource 和 async/await
TaskCompletionSource 可以用於將非同步操作轉換為 Task 對象,這使得非同步操作可以與同步代碼一樣進行操作。async/await 則是 .NET Framework 4.5 中引入的關鍵字,它可以將非同步代碼看作同步代碼,使得非同步編程更加簡單和直觀。以下是一個簡單的例子

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        Console.WriteLine("Main thread started.");

        Task<int> task = DoWorkAsync();
        int result = await task;

        Console.WriteLine("Result = {0}.", result);
        Console.WriteLine("Main thread exiting.");
    }

    static async Task<int> DoWorkAsync()
    {
        Console.WriteLine("Worker thread started.");
        await Task.Delay(2000);
        Console.WriteLine("Worker thread completed.");
        return 42;
    }
}
在這個例子中,DoWorkAsync 方法使用 async/await 非同步地執行工作,並返回一個 Task<int> 對象。Main 方法則使用 await 等待 DoWorkAsync 方法的執行,並獲取返回值。這使得非同步編程更加簡單和直觀

7.使用 Dataflow
Dataflow 是 .NET Framework 4.5 中引入的一種併發編程模型,它可以用於建立數據流管道,將多個數據處理步驟連接起來,形成一個完整的數據處理流程。Dataflow 可以處理包括非同步和同步操作在內的各種數據處理任務。以下是一個簡單的例子

using System;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;

class Program
{
    static async Task Main()
    {
        Console.WriteLine("Main thread started.");

        // 創建數據流管道
        var pipeline = new TransformBlock<int, int>(async x =>
        {
            Console.WriteLine("Thread {0} processing number {1}.", Thread.CurrentThread.ManagedThreadId, x);
            await Task.Delay(1000);
            return x * 2;
        });

        // 將多個數據處理步驟連接起來
        pipeline.LinkTo(new ActionBlock<int>(x =>
        {
            Console.WriteLine("Thread {0} processed number {1}.", Thread.CurrentThread.ManagedThreadId, x);
        }));

        // 將數據發送到管道中
        for (int i = 0; i < 10; i++)
        {
            pipeline.Post(i);
        }

        // 等待管道處理完成
        pipeline.Complete();
        await pipeline.Completion;

        Console.WriteLine("Main thread exiting.");
    }
}
在這個例子中,使用 TransformBlock 和 ActionBlock 創建了一個數據流管道,將多個數據處理步驟連接起來。在管道中發送數據時,每個數據處理步驟會非同步地處理數據,並將處理結果傳遞給下一個數據處理步驟。使用 Dataflow 可以更加方便地處理複雜的數據處理任務。

8.使用 Parallel 和 PLINQ
Parallel 和 PLINQ 是 .NET Framework 中提供的兩種併發編程模型,它們都可以用於並行執行多個操作,提高代碼的性能和併發度。以下是一個簡單的例子:

using System;
using System.Linq;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        Console.WriteLine("Main thread started.");

        int[] numbers = Enumerable.Range(1, 10).ToArray();

        // 使用 Parallel.For 並行處理數據
        Parallel.For(0, numbers.Length, i =>
        {
            Console.WriteLine("Thread {0} processing number {1}.", Thread.CurrentThread.ManagedThreadId, numbers[i]);
            Thread.Sleep(1000);
        });

        // 使用 PLINQ 並行處理數據
        var results = numbers.AsParallel().Select(x =>
        {
            Console.WriteLine("Thread {0} processing number {1}.", Thread.CurrentThread.ManagedThreadId, x);
            Thread.Sleep(1000);
            return x * 2;
        }).ToList();

        Console.WriteLine("Results: {0}.", string.Join(", ", results));
        Console.WriteLine("Main thread exiting.");
    }
}
在這個例子中,使用 Parallel.For 和 PLINQ 並行處理了一個數組中的數據。Parallel.For 使用指定的起始和結束索引並行執行多個操作,而 PLINQ 使用 AsParallel 方法將數據集合併行化,併在多個線程上執行 LINQ 操作。使用 Parallel 和 PLINQ 可以更加方便地提高代碼的性能和併發度。

9.使用 ThreadLocal
ThreadLocal 是 .NET Framework 中提供的一種線程局部存儲機制,它可以讓每個線程擁有自己獨立的數據副本,避免了線程之間的競爭和同步問題。以下是一個簡單的例子

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static ThreadLocal<int> _count = new ThreadLocal<int>(() => 0);

    static void Main()
    {
        Console.WriteLine("Main thread started.");

        // 創建多個線程並執行任務
        var tasks = new Task[3];
        for (int i = 0; i < 3; i++)
        {
            tasks[i] = Task.Factory.StartNew(() =>
            {
                _count.Value++;
                Console.WriteLine("Thread {0} count = {1}.", Thread.CurrentThread.ManagedThreadId, _count.Value);
                Thread.Sleep(1000);
                _count.Value--;
            });
        }

        Task.WaitAll(tasks);

        Console.WriteLine("Main thread exiting.");
    }
}
在這個例子中,使用 ThreadLocal 創建了一個線程局部變數,每個線程都擁有自己獨立的數據副本。在多個線程執行任務時,可以使用 _count.Value 獲取每個線程獨立的數據副本。使用 ThreadLocal 可以避免線程之間的競爭和同步問題。

10.使用 SemaphoreSlim
SemaphoreSlim 是 .NET Framework 中提供的一種輕量級信號量機制,它可以用於控制併發度和資源訪問。以下是一個簡單的例子:

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
static SemaphoreSlim _semaphore = new SemaphoreSlim(2);
static void Main()
{
    Console.WriteLine("Main thread started.");

    // 創建多個線程並執行任務
    var tasks = new Task[5];
    for (int i = 0; i < 5; i++)
    {
        tasks[i] = Task.Factory.StartNew(async () =>
        {
            Console.WriteLine("Thread {0} waiting for semaphore.", Thread.CurrentThread.ManagedThreadId);

            // 等待信號量
            await _semaphore.WaitAsync();

            try
            {
                Console.WriteLine("Thread {0} acquired semaphore.", Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(1000);
            }
            finally
            {
                // 釋放信號量
                _semaphore.Release();
                Console.WriteLine("Thread {0} released semaphore.", Thread.CurrentThread.ManagedThreadId);
            }
        });
    }

    Task.WaitAll(tasks);

    Console.WriteLine("Main thread exiting.");
}
}

在這個例子中,使用 SemaphoreSlim 創建了一個信號量,它的初始計數為 2,表示最多有兩個線程同時訪問。在多個線程執行任務時,可以使用 _semaphore.WaitAsync() 獲取信號量並等待資源,使用 _semaphore.Release() 釋放信號量。使用 SemaphoreSlim 可以控制併發度和資源訪問,避免資源競爭和死鎖問題。

總結:
.NET Core 中提供了豐富的多線程編程模型和工具,包括 Task、async/await、ThreadPool、Thread、Concurrent、Parallel、PLINQ、ThreadLocal 和 SemaphoreSlim 等。這些工具可以幫助我們更加方便地實現併發編程,提高代碼的性能和併發度。在使用多線程編程時,我們需要註意避免常見的線程安全問題,例如資源競爭、死鎖和數據不一致等。同時,我們還可以使用一些工具和技術來幫助我們發現和解決線程安全問題,例如代碼審查、單元測試和性能分析等。

解決線程安全問題是多線程編程中非常重要的一環。下麵我將介紹幾種解決線程安全問題的方法:

1.使用鎖機制:鎖機制可以確保在同一時刻只有一個線程能夠訪問共用資源。可以使用 lock 關鍵字或 Monitor 類來實現鎖機制。

2.使用互斥量:互斥量也可以用來控制對共用資源的訪問。與鎖機制不同的是,互斥量可以跨進程使用。

3.使用信號量:信號量可以用來限制對共用資源的訪問。它可以控制同時訪問共用資源的線程數量。

4.使用原子操作:原子操作是一種特殊的操作,它能夠確保在執行操作期間沒有其他線程能夠訪問同一共用資源。

5.使用併發集合:併發集合是一種特殊的數據結構,它們專門設計用來在多線程環境下安全地訪問共用資源。.NET Core 提供了許多種併發集合,例如 ConcurrentDictionary、ConcurrentQueue、ConcurrentBag 等。

6.儘量避免共用資源:如果可能的話,可以嘗試避免共用資源的使用。這樣可以減少線程之間的競爭和衝突。

7.使用線程安全的類型:在編寫多線程代碼時,可以使用線程安全的類型,例如 Interlocked、Volatile 和 ThreadLocal 等。

總之,在解決線程安全問題時,需要註意避免死鎖、饑餓、活鎖等問題,併在編寫代碼時仔細考慮多線程訪問的順序、數據的同步和共用資源的保護。同時,進行代碼審查、單元測試和性能分析等,可以幫助我們發現和解決線程安全問題。

下麵是幾個具體的示例,演示如何解決常見的線程安全問題。
1.使用鎖機制

class BankAccount
{
    private object accountLock = new object();
    private decimal balance;

    public void Deposit(decimal amount)
    {
        lock (accountLock)
        {
            balance += amount;
        }
    }

    public void Withdraw(decimal amount)
    {
        lock (accountLock)
        {
            balance -= amount;
        }
    }
}

在這個示例中,使用 lock 關鍵字來確保在 Deposit 和 Withdraw 方法執行期間,同一時刻只有一個線程能夠訪問 balance 變數。

2.使用併發集合

ConcurrentDictionary<string, int> dict = new ConcurrentDictionary<string, int>();

dict.TryAdd("one", 1);
dict.TryAdd("two", 2);
dict.TryAdd("three", 3);

foreach (var item in dict)
{
    Console.WriteLine($"{item.Key}: {item.Value}");
}
在這個示例中,使用 ConcurrentDictionary 類來存儲鍵值對。ConcurrentDictionary 是線程安全的,多個線程可以同時訪問它而不會產生競爭和衝突。

3.使用互斥量

class MyMutex
{
    private Mutex mutex = new Mutex();
    private int count = 0;

    public void AddCount()
    {
        mutex.WaitOne();
        try
        {
            count++;
        }
        finally
        {
            mutex.ReleaseMutex();
        }
    }

    public int GetCount()
    {
        mutex.WaitOne();
        try
        {
            return count;
        }
        finally
        {
            mutex.ReleaseMutex();
        }
    }
}
在這個示例中,使用 Mutex 類來實現互斥量。在 AddCount 和 GetCount 方法執行期間,同一時刻只有一個線程能夠訪問 count 變數。

總之,以上示例演示瞭如何使用不同的技術解決線程安全問題。需要根據具體情況選擇最適合的方法,同時註意代碼的效率和性能。

4.避免死鎖
1.下麵是一個可能導致死鎖的示例:

class DeadlockExample
{
    private object lockA = new object();
    private object lockB = new object();

    public void MethodA()
    {
        lock (lockA)
        {
            Console.WriteLine("MethodA acquired lockA.");
            lock (lockB)
            {
                Console.WriteLine("MethodA acquired lockB.");
            }
        }
    }

    public void MethodB()
    {
        lock (lockB)
        {
            Console.WriteLine("MethodB acquired lockB.");
            lock (lockA)
            {
                Console.WriteLine("MethodB acquired lockA.");
            }
        }
    }
}
這個示例中,兩個方法 MethodA 和 MethodB 都需要獲取兩個鎖 lockA 和 lockB。如果兩個方法在不同的線程上同時執行,那麼可能會發生死鎖,導致兩個線程互相等待對方釋放鎖,最終導致程式停滯不前。

為了避免死鎖,可以改變鎖的獲取順序。例如,在上面的示例中,可以將 MethodB 中獲取鎖的順序改為 lockA, lockB,這樣就避免了死鎖的問題。

2.使用線程安全的數據結構

如果您需要在多個線程之間共用數據,可以使用線程安全的數據結構,例如 BlockingCollection、ConcurrentQueue 和 ConcurrentStack。這些數據結構都是線程安全的,可以避免多個線程同時訪問同一個變數的問題。

例如,下麵的示例演示瞭如何使用 BlockingCollection 來實現生產者-消費者模式:

class ProducerConsumerExample
{
    private BlockingCollection<int> queue = new BlockingCollection<int>(10);

    public void Produce()
    {
        for (int i = 0; i < 20; i++)
        {
            queue.Add(i);
        }
        queue.CompleteAdding();
    }

    public void Consume()
    {
        foreach (var item in queue.GetConsumingEnumerable())
        {
            Console.WriteLine(item);
        }
    }
}
在這個示例中,一個生產者線程使用 Add 方法向隊列中添加數據,一個消費者線程使用 GetConsumingEnumerable 方法獲取數據。由於 BlockingCollection 是線程安全的,因此不需要擔心數據訪問的競爭和衝突問題。

總之,線程安全是一個非常重要的問題,需要特別註意。使用適當的技術和方法,可以避免大多數線程安全問題。

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

-Advertisement-
Play Games
更多相關文章
  • 說明 使用 VLD 記憶體泄漏檢測工具輔助開發時整理的學習筆記。 1. 使用前的準備 參考本人另一篇博客 安裝 Visual Leak Detector 下載 vld-2.5.1-setup.exe 並按步驟安裝 VLD。這一種使用方式的缺點是,當把項目拷貝到別的電腦上編譯運行時,需要按以下流程重新配 ...
  • 由於 Blazor-WebAssembly 是在瀏覽器中運行的,通常不需要執行伺服器代碼,只要有個“窩”能托管並提供相關文件的下載即可。所以,當你有一個現成的 Blazor wasm 項目,沒必要用其他語言重寫,或者你不想用 ASP.NET Core 來托管(有些大材小用了),就可以試試用 node ...
  • 1. Grid佈局 ,(Table 佈局) 兩行兩列佈局, Border 0 行 0 列預設開始 <Window x:Class="WpfApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" ...
  • 1 枚舉 enum E_MonsterType//定義了一個枚舉的變數類型 { normal1,//0 boss = 5,//5 normal2,//6,前一個自動加1 } //枚舉和switch語句天生一對,寫switch時能對枚舉類型自動補全 E_MonsterType monsterType ...
  • 用了很多年的Rapid SCADA v5,現在官網已經推出了v6,就簡單寫一下有關v6的安裝指南吧。 本指南面向Windows用戶,不適用於linux用戶 步驟 從官網下載Rapid SCADA最新的RC版本的v6,然後運行壓縮包內的ScadaSetup.exe程式。 FAQ 提示埠占用 Rapi ...
  • 簡介 本文主要介紹使用 利用 SqlSugar 來實現多資料庫的維護 ,動態建類CRUD,動態建表 ,全局過濾器 ,跨庫查詢等功能 1、創建表 SqlSugar支持了3種模式的建表(無實體建表、實體建表,實體特性建表),非常的靈活 可以多個資料庫 MYSQL MSSQL ORACLE SQLITE ...
  • 十年河東,十年河西,莫欺少年窮 學無止境,精益求精 netcore3.1控制台應用程式,引入MQTTnet 2.8版本 訂閱端: using RabbitMQ.Client; using RabbitMQ.Client.Events; using System; using System.Text; ...
  • "C#用兩個線程交替列印1-100的五種方法"是.NET工程師面試多線程常考的試題之一,主要考察對C#語法和對多線程的熟悉程度。本文將用5種方法實現這個面試題。 方法1:使用Mutex或lock 這種方法涉及使用Mutex或lock對象來同步兩個線程。其中一個線程負責列印偶數,另一個線程負責列印奇數 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...