简述

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

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

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

第二种是抛出一个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. flutter - [03] 运算符&条件表达式

    题记部分 一.算术运算符 var a=13; var b=5; 运算符 说明 示例 结果输出 + 加 print(a+b); 18 - 减 print(a-b); 8 * 乘 print(a*b); ...

  2. JavaGUI - [03] LayoutManager布局管理器

      Component中有一个方法setBounds()可以设置当前容器的位置和大小,但如果我们手动为组件设置位置和大小的话,就会造成程序的不通用性.LayoutManager布局管理器可以根据运行平 ...

  3. Top-N推荐算法 Top-N recommendation Algorithms

    引言 推荐算法是计算机专业中的一种算法,通过一些计算,能够推测用户喜欢的东西,在互联网环境中应用比较广泛.Top-N算法在生活中非常常见,比如学术论文推荐论文.音乐软件推荐歌曲等. 今天看到一篇名叫& ...

  4. 『Plotly实战指南』--折线图绘制基础篇

    在数据分析的世界中,折线图是一种不可或缺的可视化工具. 它能够清晰地展示数据随时间或其他变量的变化趋势,帮助我们快速发现数据中的模式.趋势和异常. 无论是金融市场分析.气象数据监测,还是业务增长趋势预 ...

  5. mysql frm、MYD、MYI数据文件恢复,导入MySQL中

    前言 .frm..MYI..MYD 文件分别是 MySQL 的 MyISAM存储引擎存储的表结构.索引.数据文件. 简单方法恢复数据 .frm..MYI..MYD文件如果直接以文本打开,全部会以二进制 ...

  6. go json omitempty 关键字 脱坑

    用法 大家对于 json 和 struct 之间的转换一定不陌生,为了将代码中的结构体与 json 数据解耦,通常我们会在结构体的 field 类型后加上解释说明,例如在表示一个地址的时候, json ...

  7. goland无法识别包

    新建 Go 项目时,一定要通过 "File -> New -> Project..." 方式建立,千万不要通过 "File -> Open", ...

  8. MySQL 8.0 误删了root用户怎么办

    MySQL 8.0 误删了root用户怎么办 修改配置文件 修改配置文件,让其可以无账号登录 默认的文件为: /etc/my.cnf 添加:skip-grant-tables 重启服务 service ...

  9. Centos双网卡冗余绑定

    1. 前言 关于双网卡绑定,前前后后踩过不少的坑,虽然这是 RHCE 中的一道题,但是在实践中碰到问题也够喝一壶的. 在实践中,虚拟机.物理机都做过,但是不尽相同,大部分的坑也集中在这里,本文长期更新 ...

  10. 性能优化之使用HTTP2.0

    HTTP2.0 的优势 配置 测试 怎么查看当前网站http版本 chrome HTTP协议版本检测 Chrome 控制台 window.chrome.loadTimes() { commitLoad ...