异步编程:使用线程池管理线程

C++异步编程8大核心错误解析:从std::async到协程的实战避坑指南 异步编程是现代软件开发中提升系统性能的关键技术,其核心原理是通过非阻塞操作让CPU在等待I/O时执行其他任务,从而最大化硬件利用率。在C++中,std::async、std::future等标准库组件为异步编程提供了基础支持,但错误使用会导致数据竞争、资源泄漏等严重问题。正确的异步编程模型能显著提升高并发服务的吞吐量,广泛应用于网络服务器、游戏引擎、金融交易系统等场景。本文基于资深开发者的实战经验,深入剖析C++异步编程中最致命的8个错误,涵盖std::async启动策略误用、future析构阻塞、线程局部 阅读详情

开始《异步编程:使用线程池管理线程》

示例程序:异步编程:使用线程池管理线程.rar

如今的应用程序越来越复杂,我们常常需要使用《异步编程:线程概述及使用》中提到的多线程技术来提高应用程序的响应速度。这时我们频繁的创建和销毁线程来让应用程序快速响应操作,这频繁的创建和销毁无疑会降低应用程序性能,我们可以引入缓存机制解决这个问题,此缓存机制需要解决如:缓存的大小问题、排队执行任务、调度空闲线程、按需创建新线程及销毁多余空闲线程……如今微软已经为我们提供了现成的缓存机制:线程池

线程池源自于对象池,在详细解说明线程池前让我们先来了解下何为对象池。

线程池还不是很了解的朋友可以看看这个手写线程池视频:线程池实现视频讲解

线程池实现代码代码获取,点击:线程池源码

对象池

在系统设计中,我们常常会使用到“池”的概念。Eg:数据库连接池,socket连接池,线程池,组件队列。“池”可以节省对象重复创建和初始化所耗费的时间。对那些被系统频繁请求和使用的对象,使用此机制可以提高系统运行性能。

“池”是一种“以空间换时间”的做法,我们在内存中保存一系列整装待命的对象,供人随时差遣。与系统效率相比,这些对象所占用的内存空间太微不足道了。

流程图:

对于对象池的清理通常设计两种方式:

1) 手动清理,即主动调用清理的方法。

2) 自动清理,即通过System.Threading.Timer来实现定时清理。

关键实现代码:

public sealed class ObjectPool<T> where T : ICacheObjectProxy<T>

{

    // 最大容量

    private Int32 m_maxPoolCount = 30;

    // 最小容量

    private Int32 m_minPoolCount = 5;

    // 已存容量

    private Int32 m_currentCount;

    // 空闲+被用 对象列表

    private Hashtable m_listObjects;

    // 最大空闲时间

    private int maxIdleTime = 120;

    // 定时清理对象池对象

    private Timer timer = null;

 

    /// <summary>

    /// 创建对象池

    /// </summary>

    /// <param name="maxPoolCount">最小容量</param>

    /// <param name="minPoolCount">最大容量</param>

    /// <param name="create_params">待创建的实际对象的参数</param>

    public ObjectPool(Int32 maxPoolCount, Int32 minPoolCount, Object[] create_params){ }

 

    /// <summary>

    /// 获取一个对象实例

    /// </summary>

    /// <returns>返回内部实际对象,若返回null则线程池已满</returns>

    public T GetOne(){ }

 

    /// <summary>

    /// 释放该对象池

    /// </summary>

    public void Dispose(){ }

 

    /// <summary>

    /// 将对象池中指定的对象重置并设置为空闲状态

    /// </summary>

    public void ReturnOne(T obj){ }

 

    /// <summary>

    /// 手动清理对象池

    /// </summary>

    public void ManualReleaseObject(){ }

 

    /// <summary>

    /// 自动清理对象池(对大于 最小容量 的空闲对象进行释放)

    /// </summary>

    private void AutoReleaseObject(Object obj){ }

}
通过对“对象池”的一个大体认识能帮我们更快理解线程池。

线程池ThreadPool类详解

ThreadPool静态类,为应用程序提供一个由系统管理的辅助线程池,从而使您可以集中精力于应用程序任务而不是线程管理。每个进程都有一个线程池,一个Process中只能有一个实例,它在各个应用程序域(AppDomain)是共享的。

在内部,线程池将自己的线程划分工作者线程(辅助线程)和I/O线程。前者用于执行普通的操作,后者专用于异步IO,比如文件和网络请求,注意,分类并不说明两种线程本身有差别,内部依然是一样的。

public static class ThreadPool

{

    // 将操作系统句柄绑定到System.Threading.ThreadPool。

    public static bool BindHandle(SafeHandle osHandle);

 

    // 检索由ThreadPool.GetMaxThreads(Int32,Int32)方法返回的最大线程池线程数和当前活动线程数之间的差值。

    public static void GetAvailableThreads(out int workerThreads

            , out int completionPortThreads);

 

    // 设置和检索可以同时处于活动状态的线程池请求的数目。

    // 所有大于此数目的请求将保持排队状态,直到线程池线程变为可用。

    public static bool SetMaxThreads(int workerThreads, int completionPortThreads);

    public static void GetMaxThreads(out int workerThreads, out int completionPortThreads);

    // 设置和检索线程池在新请求预测中维护的空闲线程数。

    public static bool SetMinThreads(int workerThreads, int completionPortThreads);

    public static void GetMinThreads(out int workerThreads, out int completionPortThreads);

 

    // 将方法排入队列以便执行,并指定包含该方法所用数据的对象。此方法在有线程池线程变得可用时执行。

    public static bool QueueUserWorkItem(WaitCallback callBack, object state);

    // 将重叠的 I/O 操作排队以便执行。如果成功地将此操作排队到 I/O 完成端口,则为 true;否则为 false。

    // 参数overlapped:要排队的System.Threading.NativeOverlapped结构。

    public static bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped);

    // 将指定的委托排队到线程池,但不会将调用堆栈传播到工作者线程。

    public static bool UnsafeQueueUserWorkItem(WaitCallback callBack, object state);

 

    // 注册一个等待Threading.WaitHandle的委托,并指定一个 32 位有符号整数来表示超时值(以毫秒为单位)。

    // executeOnlyOnce如果为 true,表示在调用了委托后,线程将不再在waitObject参数上等待;

    // 如果为 false,表示每次完成等待操作后都重置计时器,直到注销等待。

    public static RegisteredWaitHandle RegisterWaitForSingleObject(

            WaitHandle waitObject

            , WaitOrTimerCallback callBack, object state,

            Int millisecondsTimeOutInterval, bool executeOnlyOnce);

    public static RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(

              WaitHandle waitObject

            , WaitOrTimerCallback callBack

            , object state

            , int millisecondsTimeOutInterval

            , bool executeOnlyOnce);

    ……

}

C/C++Linux服务器开发精彩内容包括:C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,MongoDB,ZK,流媒体,P2P,Linux内核,Docker,TCP/IP,协程,DPDK多个高级知识点学习

视频资料点击:C/C++Linux服务器开发/Linux后台架构师-学习视频

1. 线程池线程数

1) 使用GetMaxThreads()和SetMaxThreads()获取和设置最大线程数

可排队到线程池的操作数仅受内存的限制;而线程池限制进程中可以同时处于活动状态的线程数(默认情况下,限制每个 CPU 可以使用 25 个工作者线程和 1,000 个 I/O 线程(根据机器CPU个数和.net framework版本的不同,这些数据可能会有变化)),所有大于此数目的请求将保持排队状态,直到线程池线程变为可用。

不建议更改线程池中的最大线程数:

a) 将线程池大小设置得太大,可能会造成更频繁的执行上下文切换及加剧资源的争用情况。

b) 其实FileStream的异步读写,异步发送接受Web请求,System.Threading.Timer定时器,甚至使用delegate的beginInvoke都会默认调用 ThreadPool,也就是说不仅你的代码可能使用到线程池,框架内部也可能使用到。

c) 一个应用程序池是一个独立的进程,拥有一个线程池,应用程序池中可以有多个WebApplication,每个运行在一个单独的AppDomain中,这些WebApplication公用一个线程池。

2) 使用GetMinThreads()和SetMinThreads()获取和设置最小空闲线程数

为避免向线程分配不必要的堆栈空间,线程池按照一定的时间间隔创建新的空闲线程(该间隔为半秒)。所以如果最小空闲线程数设置的过小,在短期内执行大量任务会因为创建新空闲线程的内置延迟导致性能瓶颈。最小空闲线程数默认值等于机器上的CPU核数,并且不建议更改最小空闲线程数。

在启动线程池时,线程池具有一个内置延迟,用于启用最小空闲线程数,以提高应用程序的吞吐量。

在线程池运行中,对于执行完任务的线程池线程,不会立即销毁,而是返回到线程池,线程池会维护最小的空闲线程数(即使应用程序所有线程都是空闲状态),以便队列任务可以立即启动。超过此最小数目的空闲线程一段时间没事做后会自己醒来终止自己,以节省系统资源。

3) 静态方法GetAvailableThreads()

通过静态方法GetAvailableThreads()返回的线程池线程的最大数目和当前活动数目之间的差值,即获取线程池中当前可用的线程数目

4) 两个参数

方法GetMaxThreads()、SetMaxThreads()、GetMinThreads()、SetMinThreads()、GetAvailableThreads()钧包含两个参数。参数workerThreads指工作者线程;参数completionPortThreads指异步 I/O 线程。

2. 排队工作项

通过调用 ThreadPool.QueueUserWorkItem 并传递 WaitCallback 委托来使用线程池。也可以通过使用 ThreadPool.RegisterWaitForSingleObject 并传递 WaitHandle(在向其发出信号或超时时,它将引发对由 WaitOrTimerCallback 委托包装的方法的调用)来将与等待操作相关的工作项排队到线程池中。若要取消等待操作(即不再执行WaitOrTimerCallback委托),可调用RegisterWaitForSingleObject()方法返回的RegisteredWaitHandle的 Unregister 方法。

如果您知道调用方的堆栈与在排队任务执行期间执行的所有安全检查不相关,则还可以使用不安全的方法 ThreadPool.UnsafeQueueUserWorkItem 和 ThreadPool.UnsafeRegisterWaitForSingleObject。QueueUserWorkItem 和 RegisterWaitForSingleObject 都会捕获调用方的堆栈,此堆栈将在线程池线程开始执行任务时合并到线程池线程的堆栈中。如果需要进行安全检查,则必须检查整个堆栈,但它还具有一定的性能开销。使用“不安全的”方法调用并不会提供绝对的安全,但它会提供更好的性能。

3. 在一个内核构造可用时调用一个方法

让一个线程不确定地等待一个内核对象进入可用状态,这对线程的内存资源来说是一种浪费。ThreadPool.RegisterWaitForSingleObject()为我们提供了一种方式:在一个内核对象变得可用的时候调用一个方法。

使用需注意:

1) WaitOrTimerCallback委托参数,该委托接受一个名为timeOut的Boolean参数。如果 WaitHandle 在指定时间内没有收到信号(即,超时),则为 true,否则为 false。回调方法可以根据timeOut的值来针对性地采取措施。

2) 名为executeOnlyOnce的Boolean参数。传true则表示线程池线程只执行回调方法一次;若传false则表示内核对象每次收到信号,线程池线程都会执行回调方法。等待一个AutoResetEvent对象时,这个功能尤其有用。

3) RegisterWaitForSingleObject()方法返回一个RegisteredWaitHandle对象的引用。这个对象标识了线程池正在它上面等待的内核对象。我们可以调用它的Unregister(WaitHandle waitObject)方法取消由RegisterWaitForSingleObject()注册的等待操作(即WaitOrTimerCallback委托不再执行)。Unregister(WaitHandle waitObject)的WaitHandle参数表示成功取消注册的等待操作后线程池会向此对象发出信号(set()),若不想收到此通知可以传递null。

示例:

private static void Example_RegisterWaitForSingleObject()

{

    // 加endWaitHandle的原因:如果执行过快退出方法会导致一些东西被释放,造成排队的任务不能执行,原因还在研究

    AutoResetEvent endWaitHandle = new AutoResetEvent(false);

 

    AutoResetEvent notificWaitHandle = new AutoResetEvent(false);

    AutoResetEvent waitHandle = new AutoResetEvent(false);

    RegisteredWaitHandle registeredWaitHandle = ThreadPool.RegisterWaitForSingleObject(

        waitHandle,

        (Object state, bool timedOut) =>

        {

            if (timedOut)

                Console.WriteLine("RegisterWaitForSingleObject因超时而执行");

            else

                Console.WriteLine("RegisterWaitForSingleObject收到WaitHandle信号");

        },

        null, TimeSpan.FromSeconds(2), true

     );

 

    // 取消等待操作(即不再执行WaitOrTimerCallback委托)

    registeredWaitHandle.Unregister(notificWaitHandle);

 

    // 通知

    ThreadPool.RegisterWaitForSingleObject(

        notificWaitHandle,

        (Object state, bool timedOut) =>

        {

            if (timedOut)

                Console.WriteLine("第一个RegisterWaitForSingleObject没有调用Unregister()");

            else

                Console.WriteLine("第一个RegisterWaitForSingleObject调用了Unregister()");

 

            endWaitHandle.Set();

        },

        null, TimeSpan.FromSeconds(4), true

     );

 

    endWaitHandle.WaitOne();

} 
执行上下文

上一小节中说到:线程池最大线程数设置过大可能会造成Windows频繁执行上下文切换,降低程序性能。对于大多数园友不会满意这样的回答,我和你一样也喜欢“知其然,再知其所以然”。

1. 上下文切换中的“上下文”是什么?

.NET中上下文太多,我最后得出的结论是:上下文切换中的上下文专指“执行上下文”。

执行上下文包括:安全上下文、同步上下文(System.Threading.SynchronizationContext)、逻辑调用上下文(System.Runtime.Messaging.CallContext)。即:安全设置(压缩栈、Thread的Principal属性和Windows身份)、宿主设置(System.Threading.HostExcecutingContextManager)以及逻辑调用上下文数据(System.Runtime.Messaging.CallContext的LogicalSetData()和LogicalGetData()方法)。

2. 何时执行“上下文切换”?

当一个“时间片”结束时,如果Windows决定再次调度同一个线程,那么Windows不会执行上下文切换。如果Windows调度了一个不同的线程,这时Windows执行线程上下文切换。

3. “上下文切换”造成的性能影响

当Windows上下文切换到另一个线程时,CPU将执行一个不同的线程,而之前线程的代码和数据还在CPU的高速缓存中,(高速缓存使CPU不必经常访问RAM,RAM的速度比CPU高速缓存慢得多),当Windows上下文切换到一个新线程时,这个新线程极有可能要执行不同的代码并访问不同的数据,这些代码和数据不在CPU的高速缓存中。因此,CPU必须访问RAM来填充它的高速缓存,以恢复高速执行状态。但是,在其“时间片”执行完后,一次新的线程上下文切换又发生了。

上下文切换所产生的开销不会换来任何内存和性能上的收益。执行上下文所需的时间取决于CPU架构和速度(即“时间片”的分配)。而填充CPU缓存所需的时间取决于系统运行的应用程序、CPU、缓存的大小以及其他各种因素。所以,无法为每一次线程上下文切换的时间开销给出一个确定的值,甚至无法给出一个估计的值。唯一确定的是,如果要构建高性能的应用程序和组件,就应该尽可能避免线程上下文切换。

除此之外,执行垃圾回收时,CLR必须挂起(暂停)所有线程,遍历它们的栈来查找根以便对堆中的对象进行标记,再次遍历它们的栈(有的对象在压缩期间发生了移动,所以要更新它们的根),再恢复所有线程。所以,减少线程的数量也会显著提升垃圾回收器的性能。每次使用一个调试器并遇到一个断点,Windows都会挂起正在调试的应用程序中的所有线程,并在单步执行或运行应用程序时恢复所有线程。因此,你用的线程越多,调试体验也就越差。

4. 监视Windows上下文切换工具

Windows实际记录了每个线程被上下文切换到的次数。可以使用像Microsoft Spy++这样的工具查看这个数据。这个工具是Visual Studio附带的一个小工具(vs按安装路径\Visual Studio 2012\Common7\Tools),如图

5. 执行上下文类详解

在《异步编程:线程概述及使用》中我提到了Thread的两个上下文,即:

1) CurrentContext 获取线程正在其中执行的当前上下文。主要用于线程内部存储数据。

2) ExecutionContext 获取一个System.Threading.ExecutionContext对象,该对象包含有关当前线程的各种上下文的信息。主要用于线程间数据共享。

其中获取到的System.Threading.ExecutionContext就是本小节要说的“执行上下文”。

public sealed class ExecutionContext : IDisposable, ISerializable

{

    public void Dispose();

    public void GetObjectData(SerializationInfo info, StreamingContext context);

 

    // 此方法对于将执行上下文从一个线程传播到另一个线程非常有用。

    public ExecutionContext CreateCopy();

    // 从当前线程捕获执行上下文的一个副本。

    public static ExecutionContext Capture();

    // 在当前线程上的指定执行上下文中运行某个方法。

    public static void Run(ExecutionContext executionContext, ContextCallback callback, object state);

 

    // 取消执行上下文在异步线程之间的流动。

    public static AsyncFlowControl SuppressFlow();

    public static bool IsFlowSuppressed();

    // RestoreFlow  撤销以前的 SuppressFlow 方法调用的影响。

    // 此方法由 SuppressFlow 方法返回的 AsyncFlowControl 结构的 Undo 方法调用。

    // 应使用 Undo 方法(而不是 RestoreFlow 方法)恢复执行上下文的流动。

    public static void RestoreFlow();

}

ExecutionContext 类提供的功能让用户代码可以在用户定义的异步点之间捕获和传输此上下文。公共语言运行时(CLR)确保在托管进程内运行时定义的异步点之间一致地传输 ExecutionContext。

每当一个线程(初始线程)使用另一个线程(辅助线程)执行任务时,CLR会将前者的执行上下文流向(复制到)辅助线程(注意这个自动流向是单方向的)。这就确保了辅助线程执行的任何操作使用的是相同的安全设置和宿主设置。还确保了初始线程的逻辑调用上下文可以在辅助线程中使用。

但执行上下文的复制会造成一定的性能影响。因为执行上下文中包含大量信息,而收集所有这些信息,再把它们复制到辅助线程,要耗费不少时间。如果辅助线程又采用了更多地辅助线程,还必须创建和初始化更多的执行上下文数据结构。

所以,为了提升应用程序性能,我们可以阻止执行上下文的流动。当然这只有在辅助线程不需要或者不访问上下文信息的时候才能进行阻止。

下面给出一个示例为了演示:

1) 在线程间共享逻辑调用上下文数据(CallContext)。

2) 为了提升性能,阻止\恢复执行上下文的流动。

3) 在当前线程上的指定执行上下文中运行某个方法。

private static void Example_ExecutionContext()

{

    CallContext.LogicalSetData("Name", "小红");

    Console.WriteLine("主线程中Name为:{0}", CallContext.LogicalGetData("Name"));

 

    // 1)   在线程间共享逻辑调用上下文数据(CallContext)。

    Console.WriteLine("1)在线程间共享逻辑调用上下文数据(CallContext)。");

    ThreadPool.QueueUserWorkItem((Object obj)

        => Console.WriteLine("ThreadPool线程中Name为:\"{0}\"", CallContext.LogicalGetData("Name")));

    Thread.Sleep(500);

    Console.WriteLine();

    // 2)   为了提升性能,取消\恢复执行上下文的流动。

    ThreadPool.UnsafeQueueUserWorkItem((Object obj)

        => Console.WriteLine("ThreadPool线程使用Unsafe异步执行方法来取消执行上下文的流动。Name为:\"{0}\""

        , CallContext.LogicalGetData("Name")), null);

    Console.WriteLine("2)为了提升性能,取消/恢复执行上下文的流动。");

    AsyncFlowControl flowControl = ExecutionContext.SuppressFlow();

    ThreadPool.QueueUserWorkItem((Object obj)

        => Console.WriteLine("(取消ExecutionContext流动)ThreadPool线程中Name为:\"{0}\"", CallContext.LogicalGetData("Name")));

    Thread.Sleep(500);

    // 恢复不推荐使用ExecutionContext.RestoreFlow()

    flowControl.Undo();

    ThreadPool.QueueUserWorkItem((Object obj)

        => Console.WriteLine("(恢复ExecutionContext流动)ThreadPool线程中Name为:\"{0}\"", CallContext.LogicalGetData("Name")));

    Thread.Sleep(500);

    Console.WriteLine();

    // 3)   在当前线程上的指定执行上下文中运行某个方法。(通过获取调用上下文数据验证)

    Console.WriteLine("3)在当前线程上的指定执行上下文中运行某个方法。(通过获取调用上下文数据验证)");

    ExecutionContext curExecutionContext = ExecutionContext.Capture();

    ExecutionContext.SuppressFlow();

    ThreadPool.QueueUserWorkItem(

        (Object obj) =>

        {

            ExecutionContext innerExecutionContext = obj as ExecutionContext;

            ExecutionContext.Run(innerExecutionContext, (Object state)

                => Console.WriteLine("ThreadPool线程中Name为:\"{0}\""<br>                       , CallContext.LogicalGetData("Name")), null);

        }

        , curExecutionContext

     );

}

结果如图:

注意:

1) 示例中“在当前线程上的指定执行上下文中运行某个方法”:代码中必须使用ExecutionContext.Capture()获取当前执行上下文的一个副本。

a) 若直接使用Thread.CurrentThread.ExecutionContext则会报“无法应用以下上下文: 跨 AppDomains 封送的上下文、不是通过捕获操作获取的上下文或已作为 Set 调用的参数的上下文。”错误。

b) 若使用Thread.CurrentThread.ExecutionContext.CreateCopy()会报“只能复制新近捕获(ExecutionContext.Capture())的上下文”。

2) 取消执行上下文流动除了使用ExecutionContext.SuppressFlow()方式外。还可以通过使用ThreadPool的UnsafeQueueUserWorkItem 和 UnsafeRegisterWaitForSingleObject来执行委托方法。原因是不安全的线程池操作不会传输压缩堆栈。每当压缩堆栈流动时,托管的主体、同步、区域设置和用户上下文也随之流动。

线程池线程中的异常

线程池线程中未处理的异常将终止进程。以下为此规则的三种例外情况: 1. 由于调用了 Abort,线程池线程中将引发ThreadAbortException。 2. 由于正在卸载应用程序域,线程池线程中将引发AppDomainUnloadedException。 3. 公共语言运行库或宿主进程将终止线程。

何时不使用线程池线程

现在大家都已经知道线程池为我们提供了方便的异步API及托管的线程管理。那么是不是任何时候都应该使用线程池线程呢?当然不是,我们还是需要“因地制宜”的,在以下几种情况下,适合于创建并管理自己的线程而不是使用线程池线程:

1. 需要前台线程。(线程池线程“始终”是后台线程)

2. 需要使线程具有特定的优先级。(线程池线程都是默认优先级,“不建议”进行修改)

3. 任务会长时间占用线程。由于线程池具有最大线程数限制,因此大量占用线程池线程可能会阻止任务启动。

4. 需要将线程放入单线程单元(STA)。(所有ThreadPool线程“始终”是多线程单元(MTA)中)

5. 需要具有与线程关联的稳定标识,或使某一线程专用于某一任务。

  本文介绍线程池以及其基础对象池,ThreadPool类的使用及注意事项,如何排队工作项到线程池,执行上下文及线程上下文传递问题……

线程池虽然为我们提供了异步操作的便利,但是它不支持对线程池中单个线程的复杂控制致使我们有些情况下会直接使用Thread。并且它对“等待”操作、“取消”操作、“延续”任务等操作比较繁琐,可能迫使你从新造轮子。微软也想到了,所以在.NET4.0的时候加入了“并行任务”并在.NET4.5中对其进行改进。

C++异步编程利器:std::packaged_task原理、应用与线程池实战 在C++并发编程中,异步任务执行与结果获取是核心需求。其基本原理是通过分离任务执行与结果同步,实现高效、安全的并行计算。std::future/std::promise机制为此提供了标准解决方案,允许一个线程承诺(promise)在未来提供结果,而另一个线程通过future等待并获取该结果,这构成了C++11以来基于任务的异步编程范式的基础技术价值。该模式极大地简化了线程间通信与同步的复杂度,避免了手动管理锁和条件变量的繁琐与易错性。其典型应用场景包括计算密集型任务的后台执行、GUI应用中的耗时操作不阻塞主 阅读详情

相关推荐

async-flow

背景:系统中有各种业务,业务设计之初,需求并不复杂,业务流程简单的同步执行即可。 对业务流程,需要支持流程断点继续执行支持连续执行支持顺序定时重试 大多数同事认为流程引擎过重。但是又非常需要一种一致的异步流程执行方案。种轻量级异步执行方案。 基本思想使用连续消息速度,在需要异步执行某流程异步的时候,向延迟消息串行发送延迟消息。系统监听到延迟消息,继续执行之前的流程。 消息丢失:使用重组处理超时更久的任务。发送消息到消息之前,向数据库保存任务。执行任务之后,更新任务状态。 为了简化实现,约定每个流程对应一个bean,每个实例对应一个方法。之后,手动推进流程(调用BeanTaskExecutor#async的方法)。流程定义,实际上相当于在代码中定义了(要改流程,需要改代码)。 MQProducer,MQConsumer用来模拟逐步替代生产者和消费者。本项目只是一个演示,没有集成的spring

5、异步流(Asynchronous Flow)

异步流 一个suspend函数异步的返回一个值,但是我们怎么返回多个异步计算的值呢?这就是kotlin流(Flows)存在的理由。 1、多个值的表示 在kotlin中可以使用集合collections来表示多个值,比如我们有个simple函数用于返回一个包含3个值的List,然后使用forEach把他们打印出来,代码如下: package com.cool.cleaner.test fun simple(): List<Int> = listOf(1, 2, 3) fun m..

1557

Pywinauto与Inspect.exe完美搭配:解决Windows应用UI自动化中的元素定位难题

本文详细介绍了如何将Pywinauto与Inspect.exe结合,以解决Windows应用UI自动化中的核心难题——元素定位。通过实战指南,阐述了如何利用Inspect.exe在UIA模式下精准侦察控件属性,并将这些信息转化为Pywinauto的稳健定位策略,从而高效自动化WPF、WinForms等现代框架构建的复杂桌面应用。

weixin_29297127的博客 520

用Windbg调试.NET程序的资源泄漏

在产品环境中的一个Windows服务出现了异常情况。这是一个基于WCF的.NET程序,它向网络应用(Web Application)提供WCF服务,同时也调用其他WCF服务以完成任务。突然,它不能响应网络应用的WCF调用。在它的日志文件中,我发现如下异常记录: System.Net.Sockets.SocketException: An operation on a socket could n

618

国产数据库迁移必看:达梦到ES的CDC方案优化全记录(含完整代码)

本文详细记录了从达梦数据库到Elasticsearch的CDC方案优化全过程,涵盖触发器方案局限性、Debezium架构优势及完整代码实现。通过Flink集成和性能调优,实现毫秒级延迟同步,显著降低数据库负载,为国产数据库迁移提供可落地的技术方案。

weixin_29079671的博客 500

ZXDoc工业级CAN总线仿真工具全解析与应用实践

CAN总线作为现代工业通信的核心协议,其仿真技术是汽车电子和工业控制领域的关键支撑。通过物理层信号模拟、协议层报文解析和应用层人机交互的三维仿真,开发者能够构建完整的硬件在环测试环境。ZXDoc作为工业级仿真工具,集成了DBC工程化管理、HMI面板设计和自动化测试脚本等核心功能,特别适用于ECU开发、产线验证等场景。工具支持ISO11898标准协议,提供错误帧注入、负载压力测试等高级特性,结合Python API可实现复杂测试用例的自动化执行。在新能源车VCU测试等实践中,通过优化采样点等参数配置,显著提升

weixin_30096975的博客 474

如何快速实现异步轮询 Web API

Web API 在处理一些耗时的请求时,往往不能让客户端一直等待响应,否则会造成连接超时或资源浪费的问题。

寒冰屋的专栏 1057

SAC-IA算法点云粗配准

SAC-IA(Sample Consensus Initial Alignment)是PCL库中的一种初始点云配准算法,它使用特征匹配和RANSAC(随机抽样一致性)框架来估计两个点云之间的初始刚性变换。其目标是通过特征描述符找到一组可靠的匹配点对,并使用这些点对估计初始对齐的变换矩阵。

twnkie的博客 1445

PocketFlow项目教程:深入理解异步处理(AsyncNode与AsyncFlow)

PocketFlow项目教程:深入理解异步处理(AsyncNode与AsyncFlow) 【免费下载链接】Tutorial-Codebase-Knowledge Turns Codebase into Easy Tutorial with AI ...

gitblog_00521的博客 567

面试官连夜整理:103道AI Agent面试题,答对一半直接加薪50%

维度AutoGPTLangGraphAutoGen控制流LLM 驱动的隐式循环代码层定义的显式状态图对话驱动的自动流转状态存储Prompt 上下文中(易丢失)结构化 State 对象(可持久化)对话历史(自动累积)分支能力依赖 LLM 的文本输出(不稳定)条件边 + 节点路由(确定性)多 Agent 对话自然分支(涌现式)回溯/回滚无原生支持,只能重新开始原生 Checkpoint 支持时间旅行无全局回滚,单 Agent 可重启人机协同仅人工在关键点介入(需要自定义)原生支持。

weixin_35774598的博客 624

中国石油招标投标网 — 中标候选人爬虫技术分析

复制真实浏览器的 TLS 握手参数(密码套件顺序、TLS 扩展、ALPN 等),绕过基于 JA3/JA3S 指纹的服务端检测。所有 API 请求体使用 RSA-1024 公钥加密,响应体使用私钥解密,HTTP body 全程为密文。库使用 urllib3 的默认 TLS 握手参数,与真实浏览器差异明显,易被 JA3 指纹检测工具识别。密钥隐藏在 CSS 文件中,这是一种隐蔽分发技术,防止简单的密钥抓取。切换次数越多,等待时间越长,最大 60 秒,避免被代理 API 限流。为空,也返回包含基础信息的空行。

猫敷雪 287

【转】1.2异步编程:使用线程池管理线程

从此图中我们会发现 .NET 与C# 的每个版本发布都是有一个“主题”。即:C#1.0托管代码→C#2.0泛型→C#3.0LINQ→C#4.0动态语言→C#5.0异步编程。现在我为最新版本的“异步编程”主题写系列分享,期待你的查看及点评。 传送门:异步编程系列目录…… 开始《异步编程:使用线程池管理线程》 示例程序:异步编程:使用线程池管理线程.rar 如今的应用程序越来越复杂,我们常常需要使用《异步编程:线程概述及使用》中提到的多线程技...

sinolover的专栏 510

CT探测器普遍采用“星形拓扑+多ADC模组”架构的TDC(时间数字转换器)设计方案

本文提出一种基于TDC反馈机制的CT探测器时钟同步方法,通过延时测量、偏差计算和动态补偿三步硬件流水线,实现高精度同步。系统采用Xilinx Kintex UltraScale FPGA内置CARRY8进位链构建3.5ps分辨率的TDC,结合多级比较器和延迟单元,形成闭环反馈控制。实验表明,在512通道、1.28m传输距离下,同步精度稳定在10ps以内,较传统方案提升两个数量级,并能动态补偿干扰抖动。该方案解决了传统方法在CT旋转腔体环境下精度不足的问题,为下一代超高清CT提供了有效的时钟同步解决方案。

whm128的博客 276

C#异步编程深度解析:从Task优化到线程池管理

异步编程是现代软件开发的核心技术,其本质是通过非阻塞方式提升系统吞吐量。在C#中,基于Task的异步模式(TAP)通过async/await语法糖简化了回调复杂性,但底层仍依赖线程池和状态机机制。理解Task与ValueTask的内存差异、线程池饥饿成因以及背压设计模式,对构建高性能应用至关重要。本文通过实际案例展示如何在高并发场景中优化异步代码,包括避免常见陷阱、合理使用BoundedChannel实现流量控制,以及通过自定义TaskScheduler解决特殊调度需求。掌握这些技术能有效提升ASP.NET

weixin_31714129的博客 298

DeepSeek 赋能智慧消防:以 AI 之力筑牢城市安全 “防火墙”

在数字化浪潮下,智慧消防系统亟需先进技术提升效能。DeepSeek 凭借 Transformer 架构与混合专家架构等核心技术,具备强大的多模态数据处理与智能决策能力,且支持本地化部署,保障数据安全。其在智慧消防领域的应用成效显著,从实现火灾风险动态感知与早期精准预警,到优化智能接警调度、设备健康管理,再到辅助应急指挥与疏散,有效降低火灾风险与事故损失。未来,DeepSeek 有望在智慧消防领域持续拓展,同时也需应对技术、数据等多方面挑战。

邓邓子的博客 1692

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

【C++11】异步编程

本文带你学习C++11实现异步编程的机制:future、async、promise、packaged_task,并设计一个异步线程池。

无聊_人的博客 1万+

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

现代 C++ 异步编程:从零实现一个高性能 ThreadPool (C++20 深度实践)

实现一个线程池不难,但实现一个既类型安全又内存无忧的线程池需要对 C++ 的底层机制有深刻理解。解决了异质任务的存储问题。解决了异步任务的生命周期问题。解决了线程资源的回收问题。这个短小精悍的实现,正是 C++ 现代化的魅力所在。版权声明:本文采用 CC BY-SA 4.0 协议,转载请注明出处。

左手是诗,右手是代码。 520

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Java异步编程之路:线程池、CompletableFuture与虚拟线程实战解析

在高并发服务中,线程是宝贵的系统资源。一条线程的创建、上下文切换和阻塞等待都会消耗大量开销,当数十万请求同时到达时,传统的“一线程一请求”模型容易让CPU空转、吞吐骤降。异步编程的核心不是让单个请求变快,而是通过线程复用和任务解耦,减少线程空等时间。线程池提供有界队列和拒绝策略实现受控并发,CompletableFuture 则通过声明式编排完成多接口并行、超时降级等复杂组合。JDK 21的虚拟线程更进一步,让同步代码也能获得近似异步的高并发效果。无论是在Java面试中梳理线程池参数与Future误用,还是

weixin_30899023的博客 254
上一篇: 23种设计模式,今天来聊聊模板模式,工厂方法模式,单例模式。
下一篇: 现场解析服务化 即时通讯方案丨网络协议,应用层协议的选择
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值