TaskCreationOptions.LongRunning 运行比可用线程数更多的任务
最近在学WebSocket,服务端需要监听多个WebSocket客户端发送的消息。
开始的解决方法是每个WebSocket客户端都添加一个线程进行监听,代码如下:
- /// <summary>
- /// 监听端口 创建WebSocket
- /// </summary>
- /// <param name="httpListener"></param>
- private void CreateWebSocket(HttpListener httpListener)
- {
- if (!httpListener.IsListening)
- throw new Exception("HttpListener未启动");
- HttpListenerContext listenerContext = httpListener.GetContextAsync().Result;
- if (!listenerContext.Request.IsWebSocketRequest)
- {
- CreateWebSocket(httpListener);
- return;
- }
- WebSocketContext webSocket = null;
- try
- {
- webSocket = new WebSocketContext(listenerContext, SubProtocol);
- }
- catch (Exception ex)
- {
- log.Error(ex);
- CreateWebSocket(HttpListener);
- return;
- }
- log.Info($"成功创建WebSocket:{webSocket.ID}");
- int workerThreads = , completionPortThreads = ;
- ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);
- if (workerThreads <= ReservedThreadsCount + || completionPortThreads <= ReservedThreadsCount + )
- {
- /**
- * 可用线程小于预留线程数量
- * 通知客户端关闭连接
- * */
- webSocket.CloseAsync(WebSocketCloseStatus.InternalServerError, "可用线程不足,无法连接").Wait();
- }
- else
- {
- if (OnReceiveMessage != null)
- webSocket.OnReceiveMessage += OnReceiveMessage;
- webSocket.OnCloseWebSocket += WebSocket_OnCloseWebSocket;
- webSocketContexts.Add(webSocket);
- // 在线程中监听客户端发送的消息
- ThreadPool.QueueUserWorkItem(new WaitCallback(p =>
- {
- (p as WebSocketContext).ReceiveMessageAsync().Wait();
- }), webSocket);
- }
- CreateWebSocket(HttpListener);
- }
在线程中添加监听代码
但是可用线程数量是有限的,先连接的客户端一直递归接收消息,导致线程无限占用,后连接上的客户端就没有线程用于监听接受消息了。
接受消息方法如下:
- /// <summary>
- /// 递归 同步接收消息
- /// </summary>
- /// <returns></returns>
- public void ReceiveMessage()
- {
- WebSocket webSocket = HttpListenerWebSocketContext.WebSocket;
- if (webSocket.State != WebSocketState.Open)
- throw new Exception("Http未握手成功,不能接受消息!");
- var byteBuffer = WebSocket.CreateServerBuffer(ReceiveBufferSize);
- WebSocketReceiveResult receiveResult = null;
- try
- {
- receiveResult = webSocket.ReceiveAsync(byteBuffer, cancellationToken).Result;
- }
- catch (WebSocketException ex)
- {
- if (ex.InnerException is HttpListenerException)
- {
- log.Error(ex);
- CloseAsync(WebSocketCloseStatus.ProtocolError, "客户端断开连接" + ex.Message).Wait(TimeSpan.FromSeconds());
- return;
- }
- else
- {
- log.Error(ex);
- CloseAsync(WebSocketCloseStatus.ProtocolError, "WebSocket 连接异常" + ex.Message).Wait(TimeSpan.FromSeconds());
- return;
- }
- }
- catch (Exception ex)
- {
- log.Error(ex);
- CloseAsync(WebSocketCloseStatus.ProtocolError, "客户端断开连接" + ex.Message).Wait(TimeSpan.FromSeconds());
- return;
- }
- if (receiveResult.CloseStatus.HasValue)
- {
- log.Info("接受到关闭消息!");
- CloseAsync(receiveResult.CloseStatus.Value, receiveResult.CloseStatusDescription).Wait(TimeSpan.FromSeconds());
- return;
- }
- byte[] bytes = new byte[receiveResult.Count];
- Array.Copy(byteBuffer.Array, bytes, bytes.Length);
- string message = Encoding.GetString(bytes);
- log.Info($"{ID}接收到消息:{message}");
- if (OnReceiveMessage != null)
- OnReceiveMessage.Invoke(this, message);
- if (!cancellationToken.IsCancellationRequested)
- ReceiveMessage();
- }
接受消息方法
这是不能接受的。
后来在Task中看到,在创建Task时可以设置TaskCreationOptions参数
该枚举有个字段LongRunning
LongRunning | 2 |
指定任务将是长时间运行的、粗粒度的操作,涉及比细化的系统更少、更大的组件。 它会向 TaskScheduler 提示,过度订阅可能是合理的。 可以通过过度订阅创建比可用硬件线程数更多的线程。 它还将提示任务计划程序:该任务需要附加线程,以使任务不阻塞本地线程池队列中其他线程或工作项的向前推动。 |
经过测试,可同时运行的任务数量的确可以超出可用线程数量。
测试如下:
没有设置 TaskCreationOptions.LongRunning 代码如下:
- /// <summary>
- /// 测试任务
- /// 只运行了9个任务
- /// </summary>
- [TestMethod]
- public void TestTask1()
- {
- var cts = new CancellationTokenSource();
- int MaxWorkerThreads = , MaxCompletionPortThreads = ;
- ThreadPool.GetMaxThreads(out MaxWorkerThreads, out MaxCompletionPortThreads);
- Console.WriteLine($"最大可用辅助线程数目为{MaxCompletionPortThreads},最大可用异步 I/O 线程数目为{MaxCompletionPortThreads}");
- MaxWorkerThreads = ;
- MaxCompletionPortThreads = ;
- Console.WriteLine(@"设置线程池中辅助线程的最大数目为{0}, 线程池中异步 I/O 线程的最大数目为{1}
- 同时运行30个长时运行线程,每个线程中运行一个同步方法,看是否30个线程是否都能运行。", MaxWorkerThreads, MaxCompletionPortThreads);
- ThreadPool.SetMaxThreads(, );
- ThreadPool.GetMaxThreads(out MaxWorkerThreads, out MaxCompletionPortThreads);
- Console.WriteLine($"最大可用辅助线程数目为{MaxCompletionPortThreads},最大可用异步 I/O 线程数目为{MaxCompletionPortThreads}");
- int count = ;
- while (count++ < )
- {
- Task.Factory.StartNew(p =>
- {
- int index = (int)p;
- int runCount = ;
- LongRunningTask($"线程{index}", runCount, cts.Token);
- }, count, cts.Token, TaskCreationOptions.None, TaskScheduler.Default);
- }
- Task.Delay(TimeSpan.FromSeconds()).Wait(TimeSpan.FromSeconds()); // 等待超时,等待任务没有执行
- cts.Cancel();
- }
- /// <summary>
- /// 长时运行任务
- /// 递归运行
- /// </summary>
- /// <param name="taskName">任务名称</param>
- /// <param name="runCount">运行次数</param>
- /// <param name="token">传播有关取消操作的通知</param>
- private void LongRunningTask(string taskName, int runCount, CancellationToken token)
- {
- PrintTask($"任务【{taskName}】线程ID【{Environment.CurrentManagedThreadId}】第【{++runCount}】次运行").Wait();
- if (!token.IsCancellationRequested)
- LongRunningTask(taskName, runCount, token);
- }
- /// <summary>
- /// 异步打印任务 等待1秒后打印消息
- /// </summary>
- /// <param name="message">消息</param>
- /// <returns></returns>
- private Task PrintTask(string message)
- {
- return Task.Factory.StartNew(() =>
- {
- Thread.Sleep();
- Console.WriteLine(message);
- });
- }
测试代码
测试结果
测试用了20秒才完成
主线程创建了一个等待10秒后完成的任务,任务等待超时20秒
说明主程序创建的任务没有执行,而是等待超时了。
设置了 TaskCreationOptions.LongRunning 代码如下:
- /// <summary>
- /// 测试长时运行任务
- /// 30个任务全部都运行了
- /// </summary>
- [TestMethod]
- public void TestTaskLongRunning()
- {
- var cts = new CancellationTokenSource();
- int MaxWorkerThreads = , MaxCompletionPortThreads = ;
- ThreadPool.GetMaxThreads(out MaxWorkerThreads, out MaxCompletionPortThreads);
- MaxWorkerThreads = ;
- MaxCompletionPortThreads = ;
- Console.WriteLine($"最大可用辅助线程数目为{MaxCompletionPortThreads},最大可用异步 I/O 线程数目为{MaxCompletionPortThreads}");
- Console.WriteLine(@"设置线程池中辅助线程的最大数目为{0}, 线程池中异步 I/O 线程的最大数目为{1}
- 同时运行30个长时运行线程,每个线程中运行一个同步方法,看是否30个线程是否都能运行。", MaxWorkerThreads, MaxCompletionPortThreads);
- ThreadPool.SetMaxThreads(, );
- ThreadPool.GetMaxThreads(out MaxWorkerThreads, out MaxCompletionPortThreads);
- Console.WriteLine($"最大可用辅助线程数目为{MaxCompletionPortThreads},最大可用异步 I/O 线程数目为{MaxCompletionPortThreads}");
- int count = ;
- while (count++ < )
- {
- Task.Factory.StartNew(p =>
- {
- int index = (int)p;
- int runCount = ;
- LongRunningTask($"线程{index}", runCount, cts.Token);
- }, count, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
- }
- Task.Delay(TimeSpan.FromSeconds()).Wait(TimeSpan.FromSeconds()); // 等待没有超时,等待任务有执行
- cts.Cancel();
- }
测试代码
测试结果:
测试用了10秒完成
主线程创建了一个等待10秒后完成的任务,任务等待超时20秒
说明主程序创建的任务立即执行了,程序等待了10秒完成。
使用TaskCreationOptions.LongRunning 需要注意的是Action必须是同步方法同时运行任务书才能超出可以用线程数量,否则不能。
例如:
- /// <summary>
- /// 测试长时运行任务
- /// 只运行了前9个任务
- /// </summary>
- [TestMethod]
- public void TestTaskLongRunning2()
- {
- var cts = new CancellationTokenSource();
- int MaxWorkerThreads = , MaxCompletionPortThreads = ;
- ThreadPool.GetMaxThreads(out MaxWorkerThreads, out MaxCompletionPortThreads);
- Console.WriteLine($"最大可用辅助线程数目为{MaxCompletionPortThreads},最大可用异步 I/O 线程数目为{MaxCompletionPortThreads}");
- MaxWorkerThreads = ;
- MaxCompletionPortThreads = ;
- Console.WriteLine(@"设置线程池中辅助线程的最大数目为{0}, 线程池中异步 I/O 线程的最大数目为{1}
- 同时运行30个长时运行线程,每个线程中运行一个异步方法,看是否30个线程是否都能运行。", MaxWorkerThreads, MaxCompletionPortThreads);
- ThreadPool.SetMaxThreads(, );
- ThreadPool.GetMaxThreads(out MaxWorkerThreads, out MaxCompletionPortThreads);
- Console.WriteLine($"最大可用辅助线程数目为{MaxCompletionPortThreads},最大可用异步 I/O 线程数目为{MaxCompletionPortThreads}");
- int count = ;
- while (count++ < )
- {
- Task.Factory.StartNew(async p =>
- {
- int index = (int)p;
- int runCount = ;
- await LongRunningTaskAsync($"线程{index}", runCount, cts.Token);
- }, count, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
- }
- Task.Delay(TimeSpan.FromSeconds()).Wait(TimeSpan.FromSeconds()); // 等待没有超时,等待任务有执行
- cts.Cancel();
- }
- /// <summary>
- /// 异步长时运行任务
- /// </summary>
- /// <param name="taskName">任务名称</param>
- /// <param name="runCount">运行次数</param>
- /// <param name="token">传播有关取消操作的通知</param>
- /// <returns></returns>
- private async Task LongRunningTaskAsync(string taskName, int runCount, CancellationToken token)
- {
- await PrintTask($"任务【{taskName}】线程ID【{Environment.CurrentManagedThreadId}】第【{++runCount}】次运行");
- if (!token.IsCancellationRequested)
- await LongRunningTaskAsync(taskName, runCount, token);
- }
测试代码
测试结果
测试用了10秒完成
主线程创建了一个等待10秒后完成的任务,任务等待超时20秒
说明主程序创建的任务立即执行了,程序等待了10秒完成。
WebSocket修改后的监听方法:
- /// <summary>
- /// 监听端口 创建WebSocket
- /// </summary>
- /// <param name="httpListener"></param>
- private void CreateWebSocket(HttpListener httpListener)
- {
- if (!httpListener.IsListening)
- throw new Exception("HttpListener未启动");
- HttpListenerContext listenerContext = httpListener.GetContext();
- if (!listenerContext.Request.IsWebSocketRequest)
- {
- CreateWebSocket(httpListener);
- return;
- }
- WebSocketContext webSocket = null;
- try
- {
- webSocket = new WebSocketContext(listenerContext, SubProtocol);
- }
- catch (Exception ex)
- {
- log.Error(ex);
- CreateWebSocket(HttpListener);
- return;
- }
- log.Info($"成功创建WebSocket:{webSocket.ID}");
- int workerThreads = , completionPortThreads = ;
- ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);
- if (OnReceiveMessage != null)
- webSocket.OnReceiveMessage += OnReceiveMessage;
- webSocket.OnCloseWebSocket += WebSocket_OnCloseWebSocket;
- Task.Factory.StartNew(() =>
- {
- webSocket.ReceiveMessage();
- }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default);
- CreateWebSocket(HttpListener);
- }
修改后的WebSocket服务可以监听超过可用线程数量的客户端
TaskCreationOptions.LongRunning 运行比可用线程数更多的任务的更多相关文章
- .NET并行计算和并发6-获取线程池的最大可用线程数
using System; using System.IO; using System.Security.Permissions; using System.Threading; class Test ...
- Jmeter任在运行,线程数上不去
问题 jmeter在运行,但是线程数上不去(本来模型设计了100个总线程,但运行时线程只能上到5,根据图上观察总共也只能运行5个线程) 之前更新了random csv插件 解决办法 查看jmeter. ...
- JMeter命令行方式运行时动态设置线程数及其他属性(动态传参)
在使用JMeter进行性能测试时,以下情况经常出现: 1.测试过程中,指定运行的线程数.指定运行循环次数不断改变: 2.访问的目标地址发生改变,端口发生改变,需要改写脚本. 上面的问题在GUI中,直接 ...
- Jvm支持的最大线程数
摘自 http://blog.csdn.net/xyls12345/article/details/26482387 JVM最大线程数 (2012-07-04 23:20:15) 转载▼ 标签: jv ...
- web应用性能测试-Tomcat 7 连接数和线程数配置
转自:http://www.jianshu.com/p/8445645b3aff 引言 这段时间折腾了哈java web应用的压力测试,部署容器是tomcat 7.期间学到了蛮多散碎的知识点,及时梳理 ...
- JVM可支持的最大线程数(转)
摘自:http://sesame.iteye.com/blog/622670 工作中碰到过这个问题好几次了,觉得有必要总结一下,所以有了这篇文章,这篇文章分为三个部分:认识问题.分析问题.解决问题. ...
- 通过设置线程池的最小线程数来提高task的效率,SetMinThreads。
http://www.cnblogs.com/Charltsing/p/taskpoolthread.html task默认对线程的调度是逐步增加的,连续多次运行并发线程,会提高占用的线程数,而等若干 ...
- JVM可支持的最大线程数
转微博,因为他也是转载 不知道原出处 一.认识问题: 首先我们通过下面这个 测试程序 来认识这个问题:运行的环境 (有必要说明一下,不同环境会有不同的结果):32位 Windows XP,Sun J ...
- Tomcat参数调优包括日志、线程数、内存【转】
[Tomcat中日志打印对性能测试的影响] 一般都提供了这样5个日志级别: ▪ Debug ▪ Info ▪ Warn ▪ Error ▪ Fatal 由于性能测试需要并发进行压力测试,如果日志级别是 ...
随机推荐
- Netty 源码(二)NioEventLoop 之 Channel 注册
Netty 源码(二)NioEventLoop 之 Channel 注册 Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html) 一 ...
- Null value was assigned to a property of primitive type setter of cn.itcast.oa.domain.Forum.topicCount
[引用http://m.blog.csdn.net/blog/u013998070/41087351] Null value was assigned to a property of primiti ...
- Squares of a Sorted Array LT977
Given an array of integers A sorted in non-decreasing order, return an array of the squares of each ...
- centos安装tomcat7.0.70
抄自:https://www.cnblogs.com/www1707/p/6592504.html apache-tomcat-7.0.70jdk-7u67-linux-x64 下载tomcathtt ...
- python3版本main.py执行产生中间__pycache__详解
__pycache__ 用python编写好一个工程,在第一次运行后,总会发现工程根目录下生成了一个__pycache__文件夹,里面是和py文件同名的各种 *.pyc 或者 *.pyo 文件. 先大 ...
- 初步认识linux的top命令
今天学习了一下top命令,强大无比啊! top命令涉及到的东西很多.用来监视系统的运行状态,top打印包括cpu.内存.进程使用情况的统计信息,还打印出进程列表. 输入top命令,不带任何参数,默认打 ...
- python之函数篇3
一:函数的定义 1)函数的简单使用,无参函数 def f1(): # 定义函数指定函数名 print("hello") # 指定功能 f1() # 调用函数,才能执行函数体里面的功 ...
- Spring框架简介
1.发明者:Rod Johnson 2.轮子理论推崇者: 2.1 轮子理论:不用重复发明轮子 2.2 IT行业:直接只用写好的代码 3.Spring框架宗旨:不重新发明技术,让原有技术使用起来更加方便 ...
- 746. Min Cost Climbing Stairs
两种方法,核心思想都一样,求出走到每一步上的最小开销,直到最后一步和倒数第二步,比较其最小值返回即可. 方法一,用一个辅助的容器 class Solution { public: int minCos ...
- 2018.12.05 codeforces 948C. Producing Snow(堆)
传送门 维护一个堆. 每次先算出一个都不弹掉的总贡献. 然后把要弹掉的弹掉,并减去它们对应的贡献. 代码: #include<bits/stdc++.h> #define ri regis ...