.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
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...
  • 目錄前言PostgreSql安裝測試額外Nuget安裝Person.cs模擬運行Navicate連postgresql解決方案Garnet為什麼要選擇Garnet而不是RedisRedis不再開源Windows版的Redis是由微軟維護的Windows Redis版本老舊,後續可能不再更新Garne ...
  • C#TMS系統代碼-聯表報表學習 領導被裁了之後很快就有人上任了,幾乎是無縫銜接,很難讓我不想到這早就決定好了。我的職責沒有任何變化。感受下來這個系統封裝程度很高,我只要會調用方法就行。這個系統交付之後不會有太多問題,更多應該是做小需求,有大的開發任務應該也是第二期的事,嗯?怎麼感覺我變成運維了?而 ...
  • 我在隨筆《EAV模型(實體-屬性-值)的設計和低代碼的處理方案(1)》中介紹了一些基本的EAV模型設計知識和基於Winform場景下低代碼(或者說無代碼)的一些實現思路,在本篇隨筆中,我們來分析一下這種針對通用業務,且只需定義就能構建業務模塊存儲和界面的解決方案,其中的數據查詢處理的操作。 ...
  • 對某個遠程伺服器啟用和設置NTP服務(Windows系統) 打開註冊表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer 將 Enabled 的值設置為 1,這將啟用NTP伺服器功 ...
  • title: Django信號與擴展:深入理解與實踐 date: 2024/5/15 22:40:52 updated: 2024/5/15 22:40:52 categories: 後端開發 tags: Django 信號 松耦合 觀察者 擴展 安全 性能 第一部分:Django信號基礎 Djan ...
  • 使用xadmin2遇到的問題&解決 環境配置: 使用的模塊版本: 關聯的包 Django 3.2.15 mysqlclient 2.2.4 xadmin 2.0.1 django-crispy-forms >= 1.6.0 django-import-export >= 0.5.1 django-r ...
  • 今天我打算整點兒不一樣的內容,通過之前學習的TransformerMap和LazyMap鏈,想搞點不一樣的,所以我關註了另外一條鏈DefaultedMap鏈,主要調用鏈為: 調用鏈詳細描述: ObjectInputStream.readObject() DefaultedMap.readObject ...
  • 後端應用級開發者該如何擁抱 AI GC?就是在這樣的一個大的浪潮下,我們的傳統的應用級開發者。我們該如何選擇職業或者是如何去快速轉型,跟上這樣的一個行業的一個浪潮? 0 AI金字塔模型 越往上它的整個難度就是職業機會也好,或者說是整個的這個運作也好,它的難度會越大,然後越往下機會就會越多,所以這是一 ...
  • @Autowired是Spring框架提供的註解,@Resource是Java EE 5規範提供的註解。 @Autowired預設按照類型自動裝配,而@Resource預設按照名稱自動裝配。 @Autowired支持@Qualifier註解來指定裝配哪一個具有相同類型的bean,而@Resourc... ...