简述

创建线程是昂贵的操作,所以为每个短暂的异步操作创建线程会产生显著的开销,线程池就是该问题的解决方案,我们事先分配一定的资源,将这些资源放入资源池,每次需要新的资源,只需从池中获取一个,而不用创建一个新的。当该资源不再被使用时,就就将其返回池中。

ThreadPool类型拥有一个QueueUserWorkItem静态方法。该静态方法接受一个委托,代表哦用户自定义的一个异步操作。在该方法被调用后,委托会进入到内部队列中,如果池中没有任何线程,将创建一个新的工作者线程(worker thread),并将队列中第一个委托放入到该工作者线程中。如果在线程池中放入新的操作,当目前的所有操作完成后,很可能只需要重用一个线程来执行这些新的操作,如果放置的新的操作过快,线程池将创建更多的线程来执行这些操作。创建太多的线程是有限制的,在这种情况下新的操作将在队列中等待直到线程池中的工作者线程有能力执行它们。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace ThreadDemo
{
class Program1
{
private delegate string RunOnThreadPool(out int threadId); private static void Callback(IAsyncResult ar)
{
Console.WriteLine("Starting a callback....");
Console.WriteLine($"State passed tt a callback:{ar.AsyncState}");
Console.WriteLine($"Is thread pool thread:{Thread.CurrentThread.IsThreadPoolThread}");
Console.WriteLine($"Thread pool worker thread id:{Thread.CurrentThread.ManagedThreadId}");
}
private static string Test(out int threadId)
{
Console.WriteLine("Starting...");
Console.WriteLine($"Is thread pool thread:{Thread.CurrentThread.IsThreadPoolThread}");
Thread.Sleep(TimeSpan.FromSeconds(2));
threadId = Thread.CurrentThread.ManagedThreadId;
return $"Thread pool worker thread id was:{threadId}";
}
public static void Main()
{
int threadId = 0;
RunOnThreadPool poolDelegate = Test;
var t = new Thread(() => Test(out threadId));
t.Start();
t.Join();
Console.WriteLine($"Thread id:{threadId}");
IAsyncResult r = poolDelegate.BeginInvoke(out threadId, Callback, "a delegate asynchronous call");
r.AsyncWaitHandle.WaitOne();
string result = poolDelegate.EndInvoke(out threadId, r);
Console.WriteLine($"Thread pool worker thread id:{threadId}");
Console.WriteLine(result);
Thread.Sleep(TimeSpan.FromSeconds(2)); }
}
}

output:

Starting...
Is thread pool thread:False
Thread id:3
Starting...
Is thread pool thread:True
Thread pool worker thread id:4
Thread pool worker thread id was:4
Starting a callback....
State passed tt a callback:a delegate asynchronous call
Is thread pool thread:True
Thread pool worker thread id:4

向线程池中放入异步操作

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace ThreadDemo
{
class Program2
{
private static void AsyncOperation(object state)
{
Console.WriteLine($"Operation state:{state ?? "(null)"}");
Console.WriteLine($"Worker thread id:{Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(TimeSpan.FromSeconds(2));
}
public static void Main()
{
const int x = 1;
const int y = 2;
const string lambdaState = "lambda state 2";
ThreadPool.QueueUserWorkItem(AsyncOperation);
Thread.Sleep(TimeSpan.FromSeconds(1));
ThreadPool.QueueUserWorkItem(AsyncOperation, "async state");
Thread.Sleep(TimeSpan.FromSeconds(1));
ThreadPool.QueueUserWorkItem(state =>
{
Console.WriteLine($"Operation state:{state}");
Console.WriteLine($"Worker thread id:{Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(TimeSpan.FromSeconds(2)); },"lambda state");
ThreadPool.QueueUserWorkItem(_ =>
{
Console.WriteLine($"Operattion statte:{x + y},{lambdaState}");
Console.WriteLine($"Worker thread id:{Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(TimeSpan.FromSeconds(2));
}, "lambda state");
Thread.Sleep(TimeSpan.FromSeconds(2));
}
}
}

output

Operation state:(null)
Worker thread id:3
Operation state:async state
Worker thread id:4
Operation state:lambda state
Worker thread id:5
Operattion statte:3,lambda state 2
Worker thread id:3

线程池与并行度

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace ThreadDemo
{
class Program3
{
static void UseThreads(int numberOfOperations)
{
using(var countDown=new CountdownEvent(numberOfOperations))
{
Console.WriteLine("Scheduling work by creating thhreads");
for(int i = 0; i < numberOfOperations; i++)
{
var thread = new Thread(() =>
{
Console.Write($"{Thread.CurrentThread.ManagedThreadId},");
Thread.Sleep(TimeSpan.FromSeconds(0.1));
countDown.Signal();
});
thread.Start();
}
countDown.Wait();
}
Console.WriteLine();
}
static void UseThreadPool(int numberOfOperations)
{
using(var countDown=new CountdownEvent(numberOfOperations))
{
Console.WriteLine("Starting work on a threadPool");
for(int i = 0; i < numberOfOperations; i++)
{
ThreadPool.QueueUserWorkItem(_ =>
{
Console.Write($"{Thread.CurrentThread.ManagedThreadId},");
Thread.Sleep(TimeSpan.FromSeconds(0.1));
countDown.Signal();
});
}
countDown.Wait();
Console.WriteLine();
}
}
public static void Main()
{
const int numberOfOperations = 500;
var sw = new Stopwatch();
sw.Start();
UseThreads(numberOfOperations);
sw.Stop();
Console.WriteLine($"Execution time using threads:{sw.ElapsedMilliseconds}"); sw.Reset();
sw.Start();
UseThreadPool(numberOfOperations);
sw.Stop();
Console.WriteLine($"Execution time using the thread pool:{sw.ElapsedMilliseconds}");
}
}
}

output:

Scheduling work by creating thhreads
3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,420,419,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,477,478,479,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500,501,502,
Execution time using threads:224
Starting work on a threadPool
503,504,506,507,509,505,510,508,505,510,506,504,508,509,503,507,508,505,504,506,509,503,510,507,504,509,503,508,510,506,507,505,508,510,506,504,503,507,509,505,503,504,506,507,509,505,508,510,504,509,508,505,503,506,507,510,508,507,504,510,511,506,509,505,503,506,504,511,505,507,510,503,508,509,504,510,505,503,506,507,509,511,508,508,506,505,507,503,511,504,510,509,506,503,504,505,508,509,507,510,511,510,504,507,509,503,505,508,506,511,508,507,509,510,505,504,506,503,511,506,503,504,505,508,509,507,510,511,510,509,505,503,506,508,507,504,511,508,506,504,507,509,511,503,505,510,503,509,507,504,511,510,508,506,505,506,508,511,510,504,509,503,507,505,507,503,510,506,504,508,509,511,505,504,511,503,510,508,509,506,505,511,503,504,509,510,508,506,505,506,510,508,509,503,511,504,505,511,504,509,510,503,508,506,505,510,504,503,509,511,508,506,505,503,508,504,506,511,510,509,505,511,510,504,508,506,503,509,505,504,509,506,510,503,511,508,505,504,509,510,508,506,511,503,505,508,510,506,509,511,504,503,505,511,508,510,504,509,506,503,505,506,511,510,508,503,509,504,505,508,504,510,509,506,511,503,507,505,510,506,509,511,503,504,508,507,505,508,511,510,506,509,503,504,507,505,509,507,511,508,503,504,510,506,505,507,511,503,504,506,510,509,508,505,504,510,509,506,503,507,508,511,505,504,508,506,507,511,503,510,509,505,506,507,509,508,511,503,504,510,505,504,509,510,511,503,508,506,507,505,508,510,504,503,509,511,507,506,505,504,511,503,507,509,510,508,506,505,508,506,507,503,511,510,509,504,505,507,509,504,510,508,511,503,506,511,508,507,506,503,504,510,509,503,506,504,508,511,510,509,507,511,504,509,503,507,510,506,508,504,507,510,509,508,511,503,506,509,506,503,511,510,508,504,507,503,511,509,506,510,504,508,507,505,503,506,509,511,510,508,504,507,505,510,506,511,509,503,504,505,508,507,510,503,506,511,509,505,507,508,504,506,509,510,511,503,508,504,507,505,510,511,503,506,509,508,504,507,505,511,503,509,510,506,504,508,512,507,513,505,506,509,503,510,511,504,512,513,505,508,
Execution time using the thread pool:6447

实现一个取消选项

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace ThreadDemo
{
class Program4
{
static void AsyncOperation1(CancellationToken token)
{
Console.WriteLine("Startting the fist task");
for(int i = 0; i < 5; i++)
{
if (token.IsCancellationRequested)
{
Console.WriteLine("The first task has been cancelled");
return;
}
Thread.Sleep(TimeSpan.FromSeconds(1));
}
Console.WriteLine("The first task has completed successfully");
}
static void AsyncOperation2(CancellationToken token)
{
try
{
Console.WriteLine("Startting the second task");
for(int i = 0; i < 5; i++)
{
token.ThrowIfCancellationRequested();
Thread.Sleep(TimeSpan.FromSeconds(1));
}
Console.WriteLine("The second task has completed successfuly");
}
catch (OperationCanceledException)
{
Console.WriteLine("The second task has been canceled");
}
}
static void AsyncOperation3(CancellationToken token)
{
bool cancellationFlag = false;
token.Register(() => cancellationFlag = true);
Console.WriteLine("Starting the third task");
for(int i = 0; i < 5; i++)
{
if (cancellationFlag)
{
Console.WriteLine("The third task has been canceled");
return;
}
Thread.Sleep(TimeSpan.FromSeconds(1));
}
Console.WriteLine("The third task has completed successully");
}
static void Main()
{
using(var cts=new CancellationTokenSource())
{
var token = cts.Token;
ThreadPool.QueueUserWorkItem(_ => AsyncOperation1(token));
Thread.Sleep(TimeSpan.FromSeconds(2));
cts.Cancel();
}
using (var cts = new CancellationTokenSource())
{
var token = cts.Token;
ThreadPool.QueueUserWorkItem(_ => AsyncOperation2(token));
Thread.Sleep(TimeSpan.FromSeconds(2));
cts.Cancel();
}
using (var cts = new CancellationTokenSource())
{
var token = cts.Token;
ThreadPool.QueueUserWorkItem(_ => AsyncOperation3(token));
Thread.Sleep(TimeSpan.FromSeconds(2));
cts.Cancel();
}
Thread.Sleep(TimeSpan.FromSeconds(2));
}
}
}

output:

Startting the fist task
Startting the second task
The first task has been cancelled
Starting the third task
The second task has been canceled
The third task has been canceled

本程序使用了三种方式来实现取消过程,第一个是轮询检查CancellationTokenIsCancellationRequested属性,如果该属性为true,则说明操作需要被取消,而CancelationTokenIsCancellationRequested属性是直接受CancellationTokenSourceCancel方法控制的。

第二种是抛出一个OperationCancelledException异常,者允许在操作之外控制取消过程。

最后一个方式就是注册一个回调 函数,当操作被取消时,在线程池将调用该回调函数。这允许链式传递一个取消逻辑到另一个异步操作中

在线程池中使用等待事件处理器及超时

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace ThreadDemo
{
class Program5
{
static void RunOperations(TimeSpan workerOperationTimeout)
{
using(var evt=new ManualResetEvent(false))
using(var cts=new CancellationTokenSource())
{
Console.WriteLine("Registering timeout operation....");
var worker = ThreadPool.RegisterWaitForSingleObject(evt,(state,isTimedOut)=>
{
WorkerOperationWait(cts, isTimedOut);
},
null,workerOperationTimeout,true);
Console.WriteLine("Starting long running operation....");
ThreadPool.QueueUserWorkItem(_=>WorkerOperation(cts.Token,evt));
Thread.Sleep(workerOperationTimeout.Add(TimeSpan.FromSeconds(2)));
worker.Unregister(evt);
} }
static void WorkerOperation(CancellationToken token,ManualResetEvent evt)
{
for(int i = 0; i < 6; i++)
{
if (token.IsCancellationRequested)
return;
Thread.Sleep(TimeSpan.FromSeconds(1));
}
evt.Set();
}
static void WorkerOperationWait(CancellationTokenSource cts,bool isTimeout)
{
if (isTimeout)
{
cts.Cancel();
Console.WriteLine("Worker operation timed out and was cancelled");
}
else
{
Console.WriteLine("Worker operation succeed");
}
}
static void Main()
{
RunOperations(TimeSpan.FromSeconds(5));
RunOperations(TimeSpan.FromSeconds(7));
}
}
}

output:

Registering timeout operation....
Starting long running operation....
Worker operation timed out and was cancelled
Registering timeout operation....
Starting long running operation....
Worker operation succeed

线程池还有个有用的方法,ThreadPool.RegisterWaitForSingleObject

 var worker = ThreadPool.RegisterWaitForSingleObject(evt,(state,isTimedOut)=>
{
WorkerOperationWait(cts, isTimedOut);
},
null,workerOperationTimeout,true);

代码中的worker是RegisteredWaitHandle类型,ThreadPool.RegisterWaitForSingleObject方法中,第一个参数是evt,即WaitHandle类型,第二个参数是回调函数(该回调函数有两个参数,第一个是object state,第二个是bool isTimedOut),第三个参数是object state,本例中无,则为null,第四个是超时判定时间,第五个参数bool executeOnlyOnce。

所以这个方法实际上做了这样的一件事:对evt注册一个回调函数,当evt收到信号(evt.Set()被调用)或者超时(在判定时间内没有调用evt.Set()),将会调用回调函数,如果收到信号,那么传递给回调函数的isTimeOut就是false,否则就是true。

在本例中,如果超时了,那么isTimedOut由于是true,则取消WorkerOperation操作,而WorkerOperation至少需要6s,因为循环了6次,每次停1s,所以,第一个给的超出时间5s,是不够的,必定超时,只能取消操作,而第二个给的超出时间是7s,是可行的,因此操作会顺利完成。


当有大量的线程必须处于阻塞状态中等待一些多线程事件发信号时,以上方式非常有用。借助于线程池的基础设施,我们无需阻塞所有的这样的线程。可以释放这些线程直到信号事件被设置。在服务器端应用程序中,这是个非常重要的应用场景,因为服务器端应用程序要求高伸缩性及高性能。

使用计时器

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace ThreadDemo
{
class Program6
{
static Timer _timer;
static void TimerOperation(DateTime start)
{
var elapsed = DateTime.Now - start;
Console.WriteLine($"{elapsed.Seconds} seconds from {start}." +
$"Timer thread pool thread id:{Thread.CurrentThread.ManagedThreadId}"); }
static void Main()
{
Console.WriteLine("Press 'Enter' to stop the timer...");
var start = DateTime.Now;
_timer = new Timer(_=>TimerOperation(start),null,TimeSpan.FromSeconds(1),TimeSpan.FromSeconds(2));
try
{
Thread.Sleep(TimeSpan.FromSeconds(6));
_timer.Change(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(4));
Console.ReadLine();
}
finally
{
_timer.Dispose();
} }
}
}

output:

Press 'Enter' to stop the timer...
1 seconds from 2022/2/13 13:49:04.Timer thread pool thread id:4
3 seconds from 2022/2/13 13:49:04.Timer thread pool thread id:4
5 seconds from 2022/2/13 13:49:04.Timer thread pool thread id:4
7 seconds from 2022/2/13 13:49:04.Timer thread pool thread id:4
11 seconds from 2022/2/13 13:49:04.Timer thread pool thread id:4
15 seconds from 2022/2/13 13:49:04.Timer thread pool thread id:4
19 seconds from 2022/2/13 13:49:04.Timer thread pool thread id:4
23 seconds from 2022/2/13 13:49:04.Timer thread pool thread id:4

Timer实例的构造函数,第一个是一个lambda表达式,将会在线程池中被执行,第二个参数是object state,本例为null,第三个参数指定了什么时候会第一次运行lambda表达式,第四个参数指定了再次调用该lambda表达式的间隔时间。

之后主线程等待6s,而后修改计时器,采用Change方法,第一个参数即多久后启动lambda表达式,第二个参数修正为每隔多久再次调用lambda表达式。

下面简单加上state:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace ThreadDemo
{
class Program6
{
static Timer _timer;
static void TimerOperation(DateTime start,object state)
{
var elapsed = DateTime.Now - start;
Console.WriteLine($"{elapsed.Seconds} seconds from {start}." +
$"Timer thread pool thread id:{Thread.CurrentThread.ManagedThreadId}");
Console.WriteLine(state.ToString());
}
static void Main()
{
Console.WriteLine("Press 'Enter' to stop the timer...");
var start = DateTime.Now;
_timer = new Timer((state)=>TimerOperation(start,"TestState"),null,TimeSpan.FromSeconds(1),TimeSpan.FromSeconds(2));
try
{
Thread.Sleep(TimeSpan.FromSeconds(6));
_timer.Change(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(4));
Console.ReadLine();
}
finally
{
_timer.Dispose();
} }
}
}

output:

Press 'Enter' to stop the timer...
1 seconds from 2022/2/13 13:56:47.Timer thread pool thread id:4
TestState
3 seconds from 2022/2/13 13:56:47.Timer thread pool thread id:4
TestState
5 seconds from 2022/2/13 13:56:47.Timer thread pool thread id:5
TestState
7 seconds from 2022/2/13 13:56:47.Timer thread pool thread id:4
TestState
11 seconds from 2022/2/13 13:56:47.Timer thread pool thread id:5
TestState
15 seconds from 2022/2/13 13:56:47.Timer thread pool thread id:4
TestState
19 seconds from 2022/2/13 13:56:47.Timer thread pool thread id:5
TestState
23 seconds from 2022/2/13 13:56:47.Timer thread pool thread id:4

可以以更复杂的方式使用计时器,比如,可以通过Timeout.Infinite值提供给计时器,一个间隔参数来只允许计时器操作一次。然后在计时器异步操作内,能够设置下一次计时器操作将被执行的时间。

使用BackgroundWorker组件

使用BackgroundWorker,可以将异步代码组织为一系列事件及事件处理器。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace ThreadDemo
{
class Program7
{
static void Main()
{
var bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true; bw.DoWork += Worker_DoWork;
bw.ProgressChanged += Worker_ProgressChanged;
bw.RunWorkerCompleted += Worker_Completed; bw.RunWorkerAsync();
Console.WriteLine("Press C to cancel work");
do
{
if (Console.ReadKey(true).KeyChar == 'C')
{
bw.CancelAsync();
}
}
while (bw.IsBusy);
} private static void Worker_Completed(object sender, RunWorkerCompletedEventArgs e)
{
Console.WriteLine($"Completed thread pool thread id:{Thread.CurrentThread.ManagedThreadId}");
if (e.Error != null)
{
Console.WriteLine($"Exception {e.Error.Message} has occured");
}
else if (e.Cancelled)
{
Console.WriteLine($"Operation has been canceled");
}
else
{
Console.WriteLine($"The answer is {e.Result}");
}
} private static void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
Console.WriteLine($"{e.ProgressPercentage} % completed" +
$"Progresss thread pool thread id:{Thread.CurrentThread.ManagedThreadId}"); } private static void Worker_DoWork(object sender, DoWorkEventArgs e)
{
Console.WriteLine($"DoWork thread pool threadd id:{Thread.CurrentThread.ManagedThreadId}");
var bw = (BackgroundWorker)sender;
for(int i = 1; i <= 100; i++)
{
if (bw.CancellationPending)
{
e.Cancel = true;
return;
}
if (i % 10 == 0)
{
bw.ReportProgress(i);
}
Thread.Sleep(TimeSpan.FromSeconds(0.1));
}
e.Result = 42;
}
}
}

output:

按下C:

Press C to cancel work
DoWork thread pool threadd id:3
10 % completedProgresss thread pool thread id:4
20 % completedProgresss thread pool thread id:5
30 % completedProgresss thread pool thread id:4
40 % completedProgresss thread pool thread id:5
50 % completedProgresss thread pool thread id:4
Completed thread pool thread id:5
Operation has been canceled

不按C:

Press C to cancel work
DoWork thread pool threadd id:3
10 % completedProgresss thread pool thread id:4
20 % completedProgresss thread pool thread id:5
30 % completedProgresss thread pool thread id:4
40 % completedProgresss thread pool thread id:5
50 % completedProgresss thread pool thread id:4
60 % completedProgresss thread pool thread id:5
70 % completedProgresss thread pool thread id:4
80 % completedProgresss thread pool thread id:5
90 % completedProgresss thread pool thread id:4
100 % completedProgresss thread pool thread id:5
Completed thread pool thread id:4
The answer is 42

bw的允许发送报告,允许取消选项均被设置为true,对三个事件也进行了注册,在dowork中一般都要做两个事,一是轮询检查是否取消,二是不断报告进度,当进度有变时,又触发进度变化事件。

这种基于事件的异步模式称为EAP(Event-based Asynchronous Pattern),是历史上第二种用来构造异步程序的方式,现在更推荐TPL,即基于任务的异步程序模式。

BackgroundWorker实际上广泛用于WPF中,通过后台工作事件处理器的代码可以直接与UI控制器交互。

C#之使用线程池的更多相关文章

  1. 多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)

    前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...

  2. C#多线程之线程池篇3

    在上一篇C#多线程之线程池篇2中,我们主要学习了线程池和并行度以及如何实现取消选项的相关知识.在这一篇中,我们主要学习如何使用等待句柄和超时.使用计时器和使用BackgroundWorker组件的相关 ...

  3. C#多线程之线程池篇2

    在上一篇C#多线程之线程池篇1中,我们主要学习了如何在线程池中调用委托以及如何在线程池中执行异步操作,在这篇中,我们将学习线程池和并行度.实现取消选项的相关知识. 三.线程池和并行度 在这一小节中,我 ...

  4. C#多线程之线程池篇1

    在C#多线程之线程池篇中,我们将学习多线程访问共享资源的一些通用的技术,我们将学习到以下知识点: 在线程池中调用委托 在线程池中执行异步操作 线程池和并行度 实现取消选项 使用等待句柄和超时 使用计时 ...

  5. NGINX引入线程池 性能提升9倍

    1. 引言 正如我们所知,NGINX采用了异步.事件驱动的方法来处理连接.这种处理方式无需(像使用传统架构的服务器一样)为每个请求创建额外的专用进程或者线程,而是在一个工作进程中处理多个连接和请求.为 ...

  6. Java线程池解析

    Java的一大优势是能完成多线程任务,对线程的封装和调度非常好,那么它又是如何实现的呢? jdk的包下和线程相关类的类图. 从上面可以看出Java的线程池主的实现类主要有两个类ThreadPoolEx ...

  7. Android线程管理之ExecutorService线程池

    前言: 上篇学习了线程Thread的使用,今天来学习一下线程池ExecutorService. 线程管理相关文章地址: Android线程管理之Thread使用总结 Android线程管理之Execu ...

  8. Android线程管理之ThreadPoolExecutor自定义线程池

    前言: 上篇主要介绍了使用线程池的好处以及ExecutorService接口,然后学习了通过Executors工厂类生成满足不同需求的简单线程池,但是有时候我们需要相对复杂的线程池的时候就需要我们自己 ...

  9. -Android -线程池 批量上传图片 -附php接收代码

    (出处:http://www.cnblogs.com/linguanh/) 目录: 1,前序 2,类特点 3,用法 4,java代码 5,php代码 1,前序 还是源于重构,看着之前为赶时间写着的碎片 ...

  10. C#多线程--线程池(ThreadPool)

    先引入一下线程池的概念: 百度百科:线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池线程都是后台线程.每个线程都使用默认的堆栈大小,以默认的优先级运行, ...

随机推荐

  1. macbookpro m3本地部署DeepSeek模型

    macbookpro m3有着十分强大的性能.在deepseek如火如荼的当下,可以尝试在本地部署并使用.还可以将自己的文档作为语料喂给deepseek,使其能成为自己专属的AI助手. 本文介绍使用o ...

  2. MySQL - 数据更新场景

    Excel文件数据更新到表A的某两个字段 Excel文件中Sheet1页有两列,一列是序号,另一列是手机号.表A中有对应的序号列和手机号列. 1.首先,使用Navicat将Excel数据导入数据库,注 ...

  3. 【论文随笔】会话推荐系统综述(A Survey on Conversational Recommender Systems)

    前言 今天读的论文为一篇于2021年5月发表在<ACM计算机调查>(ACM Computing Surveys)的论文,文章提供了对话式推荐系统(CRS)的全面综述,探讨了CRS的定义.概 ...

  4. win7系统安装mysql新建数据库/数据表及故障处理,安装mysql后net start mysql服务无法启动

    问题描述:win7系统安装mysql,安装mysql后net start mysql服务无法启动 1.下载mysql: 官网地址:https://dev.mysql.com/downloads/mys ...

  5. tsconfig.json报错问题

    tsconfig.json报错问题 前言 创建 tsconfig.json 配置文件时,VS Code 会自动检测当前项目当中是否有ts文件,若没有则报错,提示用户需要创建一个ts文件后,再去使用ty ...

  6. minikube搭建Kubernetes环境

    前言 Kubernetes 一般都运行在大规模的计算集群上,管理很严格,Kubernetes 充分考虑到了这方面的需求,提供了一些快速搭建 Kubernetes 环境的工具. minikube 它是一 ...

  7. $GOPATH/go.mod exists but should not

    开启模块支持后,并不能与GOPATH共存,所以把项目从GOPATH中移出即可

  8. 使用Win32控制台实现libevent通信

    libevent版本:libevent-2.0.22-stable 服务端: #include <string.h> #include <errno.h> #include & ...

  9. oracle 数据库服务名怎么查

    WINDOWS上,直接看 服务里的 服务名就好:Oracle SID Service ,中间的SID就是数据库服务的名称.LINUX系统下,输入env |grep SID 可以查看到,一般没换都是这个 ...

  10. Oracle DB 关于CONNECT、RESOURCE 和DBA 角色权限

    授予角色的语法: grant <object/system privilege> to <role name>; 一般情况下,在新建数据库用户后,都会习惯性的给用户授权CONN ...