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

clip_image001

         从此图中我们会发现 .NET 与C# 的每个版本发布都是有一个“主题”。即:C#1.0托管代码→C#2.0泛型→C#3.0LINQ→C#4.0动态语言→C#5.0异步编程。现在我为最新版本的“异步编程”主题写系列分享,期待你的查看及点评。

 

 

传送门:异步编程系列目录……

 

 

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

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

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

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

 

对象池

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

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

 

流程图:

image

 

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

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

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

 

关键实现代码:

1
2
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
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,比如文件和网络请求,注意,分类并不说明两种线程本身有差别,内部依然是一样的。

 

1
2
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
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);
    ……
}

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。

         示例:

1
2
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
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),如图

clip_image002

5.         执行上下文类详解

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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)         在当前线程上的指定执行上下文中运行某个方法。

 

1
2
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
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
     );
}

结果如图:

clip_image004

         注意:

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中对其进行改进,想了解“并行任务”的园友可以先看看《(译)关于Async与Await的FAQ》。

本节到此结束,感谢大家的观赏。赞的话还请多推荐啊 (*^_^*)

 

 

 

 

参考资料:《CLR via C#(第三版)》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/437331.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python matplotlib画散点图_python matplotlib库绘制散点图例题解析

假设通过爬虫你获取到了北京2016年3,10月份每天白天的最高气温&#xff08;分别位于列表a&#xff0c;b&#xff09;&#xff0c;那么此时如何寻找出气温随时间&#xff08;天&#xff09;变化的某种规律&#xff1f; a [11,17,16,11,12,11,12,6,6,7,8,9,12,15,14,17,18,21,16…

Hadoop集群HDFS各节点磁盘使用率不平衡,使用balancer做数据平衡

HDFS上各节点磁盘大小不一致&#xff0c;新增节点 数据平衡前&#xff0c;是非常不均衡的&#xff0c;某些节点已经接近90了 集群的数据平衡已经迫在眉睫&#xff0c;必须要搞一搞了。 1.设置传输速率 我这里是万兆网卡&#xff0c;就先设置100M了 在两台master上分别执行&a…

【转】1.3异步编程:线程同步基元对象

开始《异步编程&#xff1a;同步基元对象&#xff08;上&#xff09;》 示例&#xff1a;异步编程&#xff1a;线程同步基元对象.rar 如今的应用程序越来越复杂&#xff0c;我们常常需要多线程技术来提高我们应用程序的响应速度。每个线程都由自己的线程ID&#xff0c;当前指令…

怎么查询共享使用人_企业微信微盘怎么共享使用?企业微信如何设置微盘权限?...

使用企业微信办公时&#xff0c;如果有文件需要共享给员工&#xff0c;我们可以将文件上传到微盘的共享空间中&#xff0c;让员工自行下载并查看。那么在企业微信中&#xff0c;我们该如何创建共享空间呢&#xff1f;1如何创建微盘共享空间企业微信微盘共享空间分为两种&#x…

HDFS和Spark配置LZO压缩,Spark读取LZO创建正常Task数量

1.说明 为了解决&#xff0c;数据日益增长并且目前使用Snappy压缩导致的Spark读取时会出现OOM&#xff0c;并且处理起来速度过慢的问题&#xff0c;决定使用LZOIndex来解决问题。 线上Hadoop版本3.2.1&#xff0c;Spark2.3.1&#xff0c;Lzo使用最新版0.4.21 2.未解决的问题…

提供写入的数据少于指定的数据_指定范围数据的汇总

下图(左表)记录的是某公司各部门员工的月薪&#xff0c;现需要根据下图(右表)对指定范围内的月薪进行汇总1、汇总月薪在【2000-4000元】范围内的工资总额。本案例的难点是&#xff1a;【工资总额】数据即是求和列&#xff0c;也是条件列&#xff0c;那么求和函数应该怎么写呢&a…

【转】1.4异步编程:轻量级线程同步基元对象

开始《异步编程&#xff1a;同步基元对象&#xff08;下&#xff09;》 示例&#xff1a;异步编程&#xff1a;轻量级线程同步基元对象.rar 在《异步编程&#xff1a;线程同步基元对象》中我介绍了.NET4.0之前为我们提供的各种同步基元&#xff08;包括Interlocked、Monitor\l…

Windows高级编程学习笔记(一)

写在前面的话 之前学的Windows编程都是界面啊、网络编程啊之类的纯应用层面的东西&#xff0c;总是感觉而自己没有达到自己期望中的水平。什么水平呢&#xff1f;如果让你编写监控系统资源的工具&#xff0c;或者DLL注入相关软件&#xff0c;或者底层安全软件&#xff0c;可以胜…

【转】1.5异步编程:.NET4.X 数据并行

任务并行库 (TPL) 是 .NET Framework 4的 System.Threading 和 System.Threading.Tasks 命名空间中的一组公共类型和 API。TPL的目的在于简化向应用程序中添加并行性和并发性的过程&#xff0c;从而提高开发人员的工作效率。TPL会动态地按比例调节并发程度&#xff0c;以便最有…

idea全局搜索搜不全的BUG

搜不全真是坑坏我了&#xff0c;修改业务后&#xff0c;差点就卷铺盖走人了… 修改配置 打开help -> Find Action 输入registry后搜索(点击框内任意一行后&#xff0c;直接输入就是搜索) page.size 将100的值改大&#xff0c;一步到位100000

【转】1.6异步编程:IAsyncResult异步编程模型 (APM)

传送门&#xff1a;异步编程系列目录…… 大部分开发人员&#xff0c;在开发多线程应用程序时&#xff0c;都是使用ThreadPool的QueueUserWorkItem方法来发起一次简单的异步操作。然而&#xff0c;这个技术存在许多限制。最大的问题是没有一个内建的机制让你知道操作在什么时候…

Spark worker内存不足导致任务失败,报错Likely due to containers exceeding thresholds, or network issues

报错: Lost executor 33 on xx.xx.xx.152: Remote RPC client disassociated. Likely due to containers exceeding thresholds, or network issues. Check driver logs for WARN messages. 原因&#xff1a; 由于spark某节点可用内存不足导致整个任务失败&#xff0c;在执行…

【转】1.7异步编程:基于事件的异步编程模式(EAP)

传送门&#xff1a;异步编程系列目录…… 上一篇&#xff0c;我给大家介绍了“.NET1.0 IAsyncResult异步编程模型(APM)”&#xff0c;通过Begin*** 开启操作并返回IAsyncResult对象&#xff0c;使用 End*** 方法来结束操作&#xff0c;通过回调方法来做异步操作后其它事项。然…

【转】1.8异步编程:.NET 4.5 基于任务的异步编程模型(TAP)

传送门&#xff1a;异步编程系列目录…… 最近我为大家陆续介绍了“IAsyncResult异步编程模型 (APM)”和“基于事件的异步编程模式(EAP)”两种异步编程模型。在.NET4.0 中Microsoft又为我们引入了新的异步编程模型“基于任务的异步编程模型(TAP)”&#xff0c;并且推荐我们在开…

Qt: 找不到Qt5Widgets.lib

在静态编译的时候&#xff0c;提示错误&#xff1a; error: dependent ‘D:\IDE\Qt\5.4.2-mingw32-rel-static\5.4.2-mingw32-rel-static\lib\Qt5Widgets.lib 去目录下看了下&#xff0c;全部是libxxxxx.a文件&#xff0c;是linux的库文件。但是之前编译是正常的&#xff0c;这…

python3读写excel文件_python3 循环读取excel文件并写入json操作

文件内容&#xff1a;excel内容&#xff1a;代码&#xff1a; import xlrd import json import operator def read_xlsx(filename): # 打开excel文件 data1 xlrd.open_workbook(filename) # 读取第一个工作表 table data1.sheets()[0] # 统计行数 n_rows table.nrows data …

Qt:error LNK2038: 检测到“_MSC_VER”的不匹配项: 值“1600”不匹配值“1800

Visual Studio 2013生成Qt项目时报错。网上搜说是更改平台工具集&#xff0c;试了没用。退一步说我就是需要使用vs2013&#xff0c;改成其他的会不符合项目需求。于是打开了项目文件.sln&#xff0c;如下&#xff1a; 才发现目标工程的Qt版本是5.7.0&#xff0c;vs2013里面设置…

JAVA程序绑定到指定的CPU核上

由于服务器上某几个核被C程序绑定了&#xff0c;我们的java程序有的线程会使用到&#xff0c;导致C程序丢包异常&#xff0c;所以需要将JAVA程序绑定到指定的CPU核上 1.命令介绍 1.taskset命令 taskset -c <cpu核编号> <pid> #可以指定进程绑定到哪个cpu核上2.t…

【转】URN_URI_URL详解

URI&#xff0c;Uniform Resource Identifier&#xff0c;统一资源标识符。 URN&#xff0c;Uniform Resource Name&#xff0c;统一资源命名 URL&#xff0c;Uniform Resource Location&#xff0c;统一资源定位符。 URI 简单来理解就是标识/定义了一个资源&#xff0c;而 URL…

彻底弄懂Qt的编码(汉字乱码问题及相关函数作用)

测试1 新建test工程用于测试&#xff0c;main.c文件内容如下&#xff1a; #include <QCoreApplication> #include <QDebug>int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);QString str_hanzi("百度"); // 汉字QString str_ascii(&…