C# 擴展TaskScheduler實現獨立線程池,支持多任務批量處理,互不幹擾,無縫相容Task

来源:https://www.cnblogs.com/s0611163/archive/2020/06/03/13037612.html
-Advertisement-
Play Games

為什麼編寫TaskSchedulerEx類? 因為.NET預設線程池只有一個線程池,如果某個批量任務一直占著大量線程,甚至耗盡預設線程池,則會嚴重影響應用程式域中其它任務或批量任務的性能。 特點: 1、使用獨立線程池,線程池中線程分為核心線程和輔助線程,輔助線程會動態增加和釋放,且匯流排程數不大於參數 ...


    為什麼編寫TaskSchedulerEx類?

    因為.NET預設線程池只有一個線程池,如果某個批量任務一直占著大量線程,甚至耗盡預設線程池,則會嚴重影響應用程式域中其它任務或批量任務的性能。

     特點:

    1、使用獨立線程池,線程池中線程分為核心線程和輔助線程,輔助線程會動態增加和釋放,且匯流排程數不大於參數_maxThreadCount

    2、無縫相容Task,使用上和Task一樣,可以用它來實現非同步,參見:C# async await 非同步執行方法封裝 替代 BackgroundWorker

    3、隊列中尚未執行的任務可以取消

    4、通過擴展類TaskHelper實現任務分組

    5、和SmartThreadPool對比,優點是無縫相容Task類,和Task類使用沒有區別,因為它本身就是對Task、TaskScheduler的擴展,所以Task類的ContinueWith、WaitAll等方法它都支持,以及相容async、await非同步編程

    6、代碼量相當精簡,TaskSchedulerEx類只有230多行代碼

    7、池中的線程數量會根據負載自動增減,支持,但沒有SmartThreadPool智能,為了性能,使用了比較笨的方式實現,不知道大家有沒有既智能,性能又高的方案,我有一個思路,在定時器中計算每個任務執行平均耗時,然後使用公式(線程數 = CPU核心數 * ( 本地計算時間 + 等待時間 ) / 本地計算時間)來計算最佳線程數,然後按最佳線程數來動態創建線程,但這個計算過程可能會犧牲性能

     對比SmartThreadPool:

    TaskSchedulerEx類代碼(使用BlockingCollection,當線程數大於200時,CPU占用高,線程數小於100時,CPU占用正常):

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Utils
{
    public class TaskSchedulerEx : TaskScheduler, IDisposable
    {
        #region 外部方法
        [DllImport("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize")]
        public static extern int SetProcessWorkingSetSize(IntPtr process, int minSize, int maxSize);
        #endregion

        #region 變數屬性事件
        private BlockingCollection<Task> _tasks = new BlockingCollection<Task>();
        List<Thread> _threadList = new List<Thread>();
        private int _threadCount = 0;
        private int _maxThreadCount = 0;
        private int _timeOut = Timeout.Infinite;
        private int _extTimeOut = 3000;
        private Task _tempTask;
        private int _activeThreadCount = 0;
        private System.Timers.Timer _timer;
        private object _lockCreateTimer = new object();

        /// <summary>
        /// 活躍線程數
        /// </summary>
        public int ActiveThreadCount
        {
            get { return _activeThreadCount; }
        }

        /// <summary>
        /// 核心線程數
        /// </summary>
        public int CoreThreadCount
        {
            get { return _threadCount; }
        }

        /// <summary>
        /// 最大線程數
        /// </summary>
        public int MaxThreadCount
        {
            get { return _maxThreadCount; }
        }
        #endregion

        #region 構造函數
        public TaskSchedulerEx(int threadCount = 10, int maxThreadCount = 20)
        {
            _maxThreadCount = maxThreadCount;
            CreateThreads(threadCount);
        }
        #endregion

        #region override GetScheduledTasks
        protected override IEnumerable<Task> GetScheduledTasks()
        {
            return _tasks;
        }
        #endregion

        #region override TryExecuteTaskInline
        protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
        {
            return false;
        }
        #endregion

        #region override QueueTask
        protected override void QueueTask(Task task)
        {
            _tasks.Add(task);
        }
        #endregion

        #region 資源釋放
        /// <summary>
        /// 資源釋放
        /// 如果尚有任務在執行,則會在調用此方法的線程上引發System.Threading.ThreadAbortException,請使用Task.WaitAll等待任務執行完畢後,再調用該方法
        /// </summary>
        public void Dispose()
        {
            if (_timer != null)
            {
                _timer.Stop();
                _timer.Dispose();
                _timer = null;
            }

            _timeOut = 100;

            foreach (Thread item in _threadList)
            {
                item.Abort();
                Interlocked.Decrement(ref _activeThreadCount);
            }
            _threadList.Clear();

            GC.Collect();
            GC.WaitForPendingFinalizers();
            if (Environment.OSVersion.Platform == PlatformID.Win32NT)
            {
                SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
            }
        }
        #endregion

        #region 創建線程池
        /// <summary>
        /// 創建線程池
        /// </summary>
        private void CreateThreads(int? threadCount = null)
        {
            if (threadCount != null) _threadCount = threadCount.Value;
            _timeOut = Timeout.Infinite;

            for (int i = 0; i < _threadCount; i++)
            {
                Interlocked.Increment(ref _activeThreadCount);
                Thread thread = new Thread(new ThreadStart(() =>
                {
                    Task task;
                    while (_tasks.TryTake(out task, _timeOut))
                    {
                        CreateTimer();
                        TryExecuteTask(task);
                    }
                }));
                thread.IsBackground = true;
                thread.Start();
                _threadList.Add(thread);
            }
        }
        #endregion

        #region 創建線程
        /// <summary>
        /// 創建線程
        /// </summary>
        private void CreateThread()
        {
            Interlocked.Increment(ref _activeThreadCount);
            Thread thread = null;
            thread = new Thread(new ThreadStart(() =>
            {
                Task task;
                while (_tasks.TryTake(out task, _extTimeOut))
                {
                    TryExecuteTask(task);
                }
                Interlocked.Decrement(ref _activeThreadCount);
                if (_activeThreadCount == _threadCount)
                {
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                    if (Environment.OSVersion.Platform == PlatformID.Win32NT)
                    {
                        SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
                    }
                }
                if (thread != null)
                {
                    thread.Abort();
                    thread = null;
                }
            }));
            thread.IsBackground = true;
            thread.Start();
        }
        #endregion

        #region 創建定時器
        private void CreateTimer()
        {
            if (_timer == null) //_timer不為空時,跳過,不走lock,提升性能
            {
                lock (_lockCreateTimer)
                {
                    if (_timer == null)
                    {
                        int interval = 20;
                        _timer = new System.Timers.Timer();
                        _timer.Interval = 1000;
                        _timer.Elapsed += (s, e) =>
                        {
                            if (_timer.Interval != interval) _timer.Interval = interval;

                            if (_activeThreadCount >= _threadCount && _activeThreadCount < _maxThreadCount)
                            {
                                if (_tasks.Count > 0)
                                {
                                    CreateThread();
                                }
                                else
                                {
                                    if (_timer != null)
                                    {
                                        _timer.Stop();
                                        _timer.Dispose();
                                        _timer = null;
                                    }
                                }
                            }
                        };
                        _timer.Start();
                    }
                }
            }
        }
        #endregion

        #region 全部取消
        /// <summary>
        /// 全部取消
        /// 當前正在執行的任務無法取消,取消的只是後續任務,相當於AbortAll
        /// </summary>
        public void CancelAll()
        {
            while (_tasks.TryTake(out _tempTask)) { }
        }
        #endregion

    }
}
View Code

    TaskSchedulerEx類代碼(使用ConcurrentQueue,測試500個線程,CPU占用0%-2%,正常):

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Utils
{
    public class TaskSchedulerEx : TaskScheduler, IDisposable
    {
        #region 外部方法
        [DllImport("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize")]
        public static extern int SetProcessWorkingSetSize(IntPtr process, int minSize, int maxSize);
        #endregion

        #region 變數屬性事件
        private ConcurrentQueue<Task> _tasks = new ConcurrentQueue<Task>();
        List<Thread> _threadList = new List<Thread>();
        private int _threadCount = 0;
        private int _maxThreadCount = 0;
        private int _timeOut = Timeout.Infinite;
        private int _extTimeOut = 20000;
        private Task _tempTask;
        private int _activeThreadCount = 0;
        private System.Timers.Timer _timer;
        private object _lockCreateTimer = new object();
        private bool _run = true;
        private SpinWait _spinWait = new SpinWait();

        /// <summary>
        /// 活躍線程數
        /// </summary>
        public int ActiveThreadCount
        {
            get { return _activeThreadCount; }
        }

        /// <summary>
        /// 核心線程數
        /// </summary>
        public int CoreThreadCount
        {
            get { return _threadCount; }
        }

        /// <summary>
        /// 最大線程數
        /// </summary>
        public int MaxThreadCount
        {
            get { return _maxThreadCount; }
        }
        #endregion

        #region 構造函數
        public TaskSchedulerEx(int threadCount = 10, int maxThreadCount = 20)
        {
            _maxThreadCount = maxThreadCount;
            CreateThreads(threadCount);
        }
        #endregion

        #region override GetScheduledTasks
        protected override IEnumerable<Task> GetScheduledTasks()
        {
            return _tasks;
        }
        #endregion

        #region override TryExecuteTaskInline
        protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
        {
            return false;
        }
        #endregion

        #region override QueueTask
        protected override void QueueTask(Task task)
        {
            _tasks.Enqueue(task);
        }
        #endregion

        #region 資源釋放
        /// <summary>
        /// 資源釋放
        /// 如果尚有任務在執行,則會在調用此方法的線程上引發System.Threading.ThreadAbortException,請使用Task.WaitAll等待任務執行完畢後,再調用該方法
        /// </summary>
        public void Dispose()
        {
            _run = false;

            if (_timer != null)
            {
                _timer.Stop();
                _timer.Dispose();
                _timer = null;
            }

            _timeOut = 100;

            foreach (Thread item in _threadList)
            {
                item.Abort();
                Interlocked.Decrement(ref _activeThreadCount);
            }
            _threadList.Clear();

            GC.Collect();
            GC.WaitForPendingFinalizers();
            if (Environment.OSVersion.Platform == PlatformID.Win32NT)
            {
                SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
            }
        }
        #endregion

        #region 創建線程池
        /// <summary>
        /// 創建線程池
        /// </summary>
        private void CreateThreads(int? threadCount = null)
        {
            if (threadCount != null) _threadCount = threadCount.Value;
            _timeOut = Timeout.Infinite;

            for (int i = 0; i < _threadCount; i++)
            {
                Interlocked.Increment(ref _activeThreadCount);
                Thread thread = new Thread(new ThreadStart(() =>
                {
                    Task task;
                    while (_run)
                    {
                        if (_tasks.TryDequeue(out task))
                        {
                            CreateTimer();
                            TryExecuteTask(task);
                        }
                        else
                        {
                            Thread.Sleep(10);
                        }
                    }
                }));
                thread.IsBackground = true;
                thread.Start();
                _threadList.Add(thread);
            }
        }
        #endregion

        #region 創建線程
        /// <summary>
        /// 創建線程
        /// </summary>
        private void CreateThread()
        {
            Interlocked.Increment(ref _activeThreadCount);
            Thread thread = null;
            thread = new Thread(new ThreadStart(() =>
            {
                Task task;
                DateTime dt = DateTime.Now;
                while (_run && DateTime.Now.Subtract(dt).TotalMilliseconds < _extTimeOut)
                {
                    if (_tasks.TryDequeue(out task))
                    {
                        TryExecuteTask(task);
                        dt = DateTime.Now;
                    }
                    else
                    {
                        Thread.Sleep(100);
                    }
                }
                Interlocked.Decrement(ref _activeThreadCount);
                if (_activeThreadCount == _threadCount)
                {
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                    if (Environment.OSVersion.Platform == PlatformID.Win32NT)
                    {
                        SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
                    }
                }
                if (thread != null)
                {
                    thread.Abort();
                    thread = null;
                }
            }));
            thread.IsBackground = true;
            thread.Start();
        }
        #endregion

        #region 創建定時器
        private void CreateTimer()
        {
            if (_timer == null) //_timer不為空時,跳過,不走lock,提升性能
            {
                lock (_lockCreateTimer)
                {
                    if (_timer == null)
                    {
                        int interval = 20;
                        _timer = new System.Timers.Timer();
                        _timer.Interval = 500;
                        _timer.Elapsed += (s, e) =>
                        {
                            if (_timer.Interval != interval) _timer.Interval = interval;

                            if (_activeThreadCount >= _threadCount && _activeThreadCount < _maxThreadCount)
                            {
                                if (_tasks.Count > 0)
                                {
                                    CreateThread();
                                }
                                else
                                {
                                    if (_timer != null)
                                    {
                                        _timer.Stop();
                                        _timer.Dispose();
                                        _timer = null;
                                    }
                                }
                            }
                        };
                        _timer.Start();
                    }
                }
            }
        }
        #endregion

        #region 全部取消
        /// <summary>
        /// 全部取消
        /// 當前正在執行的任務無法取消,取消的只是後續任務,相當於AbortAll
        /// </summary>
        public void CancelAll()
        {
            while (_tasks.TryDequeue(out _tempTask)) { }
        }
        #endregion

    }
}
View Code

     RunHelper類代碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Utils
{
    /// <summary>
    /// 線程工具類
    /// </summary>
    public static class RunHelper
    {
        #region 變數屬性事件

        #endregion

        #region 線程中執行
        /// <summary>
        /// 線程中執行
        /// </summary>
        public static Task Run(this TaskScheduler scheduler, Action<object> doWork, object arg = null, Action<Exception> errorAction = null)
        {
            return Task.Factory.StartNew((obj) =>
            {
                try
                {
                    doWork(obj);
                }
                catch (Exception ex)
                {
                    if (errorAction != null) errorAction(ex);
                    LogUtil.Error(ex, "ThreadUtil.Run錯誤");
                }
            }, arg, CancellationToken.None, TaskCreationOptions.None, scheduler);
        }
        #endregion

        #region 線程中執行
        /// <summary>
        /// 線程中執行
        /// </summary>
        public static Task Run(this TaskScheduler scheduler, Action doWork, Action<Exception> errorAction = null)
        {
            return Task.Factory.StartNew(() =>
            {
                try
                {
                    doWork();
                }
                catch (Exception ex)
                {
                    if (errorAction != null) errorAction(ex);
                    LogUtil.Error(ex, "ThreadUtil.Run錯誤");
                }
            }, CancellationToken.None, TaskCreationOptions.None, scheduler);
        }
        #endregion

        #region 線程中執行
        /// <summary>
        /// 線程中執行
        /// </summary>
        public static Task<T> Run<T>(this TaskScheduler scheduler, Func<object, T> doWork, object arg = null, Action<Exception> errorAction = null)
        {
            return Task.Factory.StartNew<T>((obj) =>
            {
                try
                {
                    return doWork(obj);
                }
                catch (Exception ex)
                {
                    if (errorAction != null) errorAction(ex);
                    LogUtil.Error(ex, "ThreadUtil.Run錯誤");
                    return default(T);
                }
            }, arg, CancellationToken.None, TaskCreationOptions.None, scheduler);
        }
        #endregion

        #region 線程中執行
        /// <summary>
        /// 線程中執行
        /// </summary>
        public static Task<T> Run<T>(this TaskScheduler scheduler, Func<T> doWork, Action<Exception> errorAction = null)
        {
            return Task.Factory.StartNew<T>(() =>
            {
                try
                {
                    return doWork();
                }
                catch (Exception ex)
                {
                    if (errorAction != null) errorAction(ex);
                    LogUtil.Error(ex, "ThreadUtil.Run錯誤");
                    return default(T);
                }
            }, CancellationToken.None, TaskCreationOptions.None, scheduler);
        }
        #endregion

        #region 線程中執行
        /// <summary>
        /// 線程中執行
        /// </summary>
        public static async Task<T> RunAsync<T>(this TaskScheduler scheduler, Func<object, T> doWork, object arg = null, Action<Exception> errorAction = null)
        {
            return await Task.Factory.StartNew<T>((obj) =>
            {
                try
                {
                    return doWork(obj);
                }
                catch (Exception ex)
                {
                    if (errorAction != null) errorAction(ex);
                    LogUtil.Error(ex, "ThreadUtil.Run錯誤");
                    return default(T);
                }
            }, arg, CancellationToken.None, TaskCreationOptions.None, scheduler);
        }
        #endregion

        #region 線程中執行
        /// <summary>
        /// 線程中執行
        /// </summary>
        public static async Task<T> RunAsync<T>(this TaskScheduler scheduler, Func<T> doWork, Action<Exception> errorAction = null)
        {
            return await Task.Factory.StartNew<T>(() =>
            {
                try
                {
                    return doWork();
                }
                catch (Exception ex)
                {
                    if (errorAction != null) errorAction(ex);
                    LogUtil.Error(ex, "ThreadUtil.Run錯誤");
                    return default(T);
                }
            }, CancellationToken.None, TaskCreationOptions.None, scheduler);
        }
        #endregion

    }
}
View Code

    TaskHelper擴展類(代碼中LimitedTaskScheduler改為TaskSchedulerEx即可)(這個任務分類有點多,每個任務分類的核心線程一般是不釋放的,一直占著線程,算不算濫用):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Utils
{
    /// <summary>
    /// Task幫助類基類
    /// </summary>
    public class TaskHelper
    {
        #region UI任務
        private static LimitedTaskScheduler _UITask;
        /// <summary>
        /// UI任務(4個線程)
        /// </summary>
        public static LimitedTaskScheduler UITask
        {
            get
            {
                if (_UITask == null) _UITask = new LimitedTaskScheduler(4);
                return _UITask;
            }
        }
        #endregion

        #region 菜單任務
        private static LimitedTaskScheduler _MenuTask;
        /// <summary>
        /// 菜單任務
        /// </summary>
        public static LimitedTaskScheduler MenuTask
        {
            get
            {
                if (_MenuTask == null) _MenuTask = new LimitedTaskScheduler(2);
                return _MenuTask;
            }
        }
        #endregion

        #region 計算任務
        private static LimitedTaskScheduler _CalcTask;
        /// <summary>
        /// 計算任務(8個線程)
        /// </summary>
        public static LimitedTaskScheduler CalcTask
        {
            get
            {
                if (_CalcTask == null) _CalcTask = new LimitedTaskScheduler(8);
                return _CalcTask;
            }
        }
        #endregion

        #region 網路請求
        private static LimitedTaskScheduler _RequestTask;
        /// <summary>
        /// 網路請求(32個線程)
        /// </summary>
        public static LimitedTaskScheduler RequestTask
        {
            get
            {
                if (_RequestTask == null) _RequestTask = new LimitedTaskScheduler(32);
                return _RequestTask;
            }
        }
        #endregion

        #region 資料庫任務
        private static LimitedTaskScheduler _DBTask;
        /// <summary>
        /// 資料庫任務(32個線程)
        /// </summary>
        public static LimitedTaskScheduler DBTask
        {
            get
            {
                if (_DBTask == null) _DBTask = new LimitedTaskScheduler(32);
                return _DBTask;
            }
        }
        #endregion

        #region IO任務
        private static LimitedTaskScheduler _IOTask;
        /// <summary>
        /// IO任務(8個線程)
        /// </summary>
        public static LimitedTaskScheduler IOTask
        {
            get
            {
                if (_IOTask == null) _IOTask = new LimitedTaskScheduler(8);
                return _IOTask;
            }
        }
        #endregion

        #region 首頁任務
        private static LimitedTaskScheduler _MainPageTask;
        /// <summary>
        /// 首頁任務(16個線程)
        /// </summary>
        public static Limit

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

-Advertisement-
Play Games
更多相關文章
  • 轉載https://www.jianshu.com/p/5d27888e7c93#!/xh #!/usr/bin/env python # -*- coding=utf-8 -*- """ AES加密解密工具類 @author jzx @date 2018/10/24 此工具類加密解密結果與 htt ...
  • 什麼是sam 轉換 Single Abstract Method 實際上這是java8中提出的概念,你就把他理解為是一個方法的介面的就可以了 看一下我們每天都在使用的線程池 ExecutorService executorService= Executors.newScheduledThreadPo ...
  • 結合 AOP 輕鬆處理事件發佈處理日誌 Intro 前段時間,實現了 EventBus 以及 EventQueue 基於 Event 的事件處理,但是沒有做日誌(EventLog)相關的部分,原本想增加兩個介面, 處理事件發佈日誌和事件處理日誌,最近用了 AOP 的思想處理了 EntityFrame ...
  • Microsoft.AspNetCore.Mvc.Versioning //引入程式集 .net core 下麵api的版本控製作用不需要多說,可以查閱https://www.cnblogs.com/dc20181010/p/11313738.html 普通的版本控制一般是通過鏈接、header此類 ...
  • 系列文章 基於 abp vNext 和 .NET Core 開發博客項目 - 使用 abp cli 搭建項目 基於 abp vNext 和 .NET Core 開發博客項目 - 給項目瘦身,讓它跑起來 基於 abp vNext 和 .NET Core 開發博客項目 - 完善與美化,Swagger登場 ...
  • 引用NModbus 在NuGet搜索NModbus,添加引用。 封裝ModbusTcp類 public class ModbusTCP { private ModbusFactory modbusFactory; private IModbusMaster master; private TcpCl ...
  • 參考文檔: https://www.cnblogs.com/liaods/p/10101513.html https://www.cnblogs.com/zyz-Notes/p/12030281.html 本示例使用MVC項目做演示(不推薦,推薦直接用WebAPI),框架版本使用 4.6.2 為了支 ...
  • 前幾天,公眾號後臺有朋友在問Core的中間件,所以專門抽時間整理了這樣一篇文章。 一、前言 中間件(Middleware)最初是一個機械上的概念,說的是兩個不同的運動結構中間的連接件。後來這個概念延伸到軟體行業,大家把應用操作系統和電腦硬體之間過渡的軟體或系統稱之為中間件,比方驅動程式,就是一個典型 ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...