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 由于性能测试需要并发进行压力测试,如果日志级别是 ...
随机推荐
- wireshark源码分析 一
因为手头的项目需要识别应用层协议,于是想到了wireshark,打算在项目中集成wireshark协议分析代码.在官网上下了最新版的wireshark源代码,我的天啊,200多M,这么多代码文件怎么看 ...
- Alpha 冲刺 (1/10)
队名 火箭少男100 组长博客 林燊大哥 作业博客 Alpha 冲鸭! 成员冲刺阶段情况 林燊(组长) 过去两天完成了哪些任务 协调各成员之间的工作,对多个目标检测及文字识别模型进行评估.实验,选取较 ...
- 乘积最大(NOIP2000&NOIP水题测试(2017082301))
题目链接:乘积最大 这道题显然是道区间dp. 难度不是很大. 思路也很清晰. 我们设计一个三维状态. ans[l][r][k] 这里表示在闭区间[l,r]上操作k次的最大值. 操作就是加乘号. 转移也 ...
- 双向循环链表涉及双向指针的基本操作(C语言)
链表大概分为有无头指针,有无尾指针,是否循环,单向还是双向, 这些都很简单,前提是你要把指针和单链表理解透彻.这些都是基于单链表 的变形,要根据实际问题,选择链表的类型. 头指针的指针域储存着储存头节 ...
- [转]urllib模块urlretrieve方法
直接将远程数据下载到本地 info: urllib.urlretrieve(url[, filename[, reporthook[, data]]])参数说明:url:外部或者本地urlfilena ...
- 微信小程序组件的使用
1.在page同级目录下新建components文件夹,然后新建目录test,新建组件test 2.新建在page目录下新建目录,然后新建page页面.注意:每新建一个页面,都要修改app.json文 ...
- mysql mybatis useGeneratedKeys Field 'ID' doesn't have a default value的问题
原因是:创建表时没有让id自动增长: CREATE TABLE `STORAGE_VIRTUAL` ( `ID` ) NOT NULL AUTO_INCREMENT, `STORAGE_ID` ) N ...
- HDU 5656 CA Loves GCD (容斥)
题意:给定一个数组,每次他会从中选出若干个(至少一个数),求出所有数的GCD然后放回去,为了使自己不会无聊,会把每种不同的选法都选一遍,想知道他得到的所有GCD的和是多少. 析:枚举gcd,然后求每个 ...
- Struts2--HelloWord
Struts2官网 官网指南 官网下载Struts2.5-min-lib.zip解压把lib目录下jar包拷贝到web项目lib目录下 配置web.xml文件 <filter> <f ...
- js中的事件代理(委托)
1,什么是事件委托:通俗的讲,事件就是onclick,onmouseover,onmouseout,等就是事件,委托呢,就是让别人来做,这个事件本来是加在某些元素上的,然而你却加到别人身上来做,完成这 ...