前言

我们知道我们的task async 和 await 是基于线程池进行调度的。

但是async 和 await 也就是使用了默认的task调度,让其在线程池中运行。

但是线程池是榨干机器性能为本质的,但是有时候我们运行一些我们自己的需求,比如控制一下线程数(因为并不是线程数越高,就能有更高的性能),控制一下cpu使用,避免cpu使用太高。

正文

首先我们需要一个队列,因为我们需要让task进行保存到某个地方,这里选择队列,因为它简单,也一般符合我们先进先出(先到先运行)的想法。

public sealed class BlockingQueue<T>
{
private readonly Queue<T> _queue = new Queue<T>(); private readonly object _lock = new object(); private readonly Semaphore _pool= new Semaphore(0, int.MaxValue); public void Enqueue(T item)
{
lock (_lock)
{
_queue.Enqueue(item);
}
} public T Dequeue()
{
_pool.WaitOne();
lock(_lock)
{
return _queue.Dequeue();
}
}
}

实现一个队列,那么希望是线程安全的,所以要给其进出加上lock。

同时希望,如果队列中为空的时候能够进行等待,不至于一直去轮询。

这里使用的是Semaphore,线程信号量,这个在后面会介绍到。

然后就到了实现线程池调度的时候:

public class TaskThreadPool : TaskScheduler, IDisposable
{
private readonly BlockingQueue<Task> _queue = new BlockingQueue<Task>(); private Thread[] _threads;
private bool _disposed;
private readonly object _lock = new object(); public int ThreadCount { get; } public TaskThreadPool(int threadCount, bool isBackground = false)
{
if (threadCount < 1)
{
throw new ArgumentOutOfRangeException(nameof(threadCount), "Must be at least 1");
} ThreadCount = threadCount;
_threads = new Thread[threadCount];
for (int i = 0; i < threadCount; i++)
{
_threads[i] = new Thread(ExcuteTasks)
{
IsBackground = false
};
_threads[i].Start();
}
} public Task Run(Action action) =>
Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, this); private void ExcuteTasks()
{
while (true)
{
var task = _queue.Dequeue();
if (task == null)
{
return;
} TryExecuteTask(task);
}
} protected override IEnumerable<Task>? GetScheduledTasks()
{
return _queue.ToArray();
} protected override void QueueTask(Task task)
{
_queue.Enqueue(task);
} protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
if (_disposed)
{
throw new ObjectDisposedException(typeof(TaskThreadPool).FullName);
} return !taskWasPreviouslyQueued && TryExecuteTask(task);
} public void Dispose()
{
lock (_lock)
{
if (_disposed)
{
return;
} _disposed = true;
} for (int i = 0; i < _threads.Length; i++)
_queue.Enqueue(null); foreach (var thread in _threads)
thread.Join(); _threads = null;
_queue.Dispose();
}
}

代码也很简单常规,就是初始化多少个线程作为线程池,然后Task排队运行就行了,记得要释放资源。

这里dispose让其他线程进行停止的信号为:_queue.Enqueue(null).

private void ExcuteTasks()
{
while (true)
{
var task = _queue.Dequeue();
if (task == null)
{
return;
} TryExecuteTask(task);
}
}

其他线程消费到null,那么就应该停止了。

然后有一个run方法,可以直接让action,放进来运行:

public Task Run(Action action) =>
Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, this);

总结一下基本思路:

  1. 需要实现TaskScheduler,这样可以避免自己写一些任务运行的逻辑控制

  2. 因为使用了信号量,所以BlockingQueue,然后 TaskThreadPool 需要使用到BlockingQueue,所以需要加上IDispose

  3. 需要控制线程数,并在对象销毁的时候禁止新的task进入,运行完已经加入队列的任务

  4. 需要有一个run方法,这样对外提供方便

考虑到信号量的释放,那么也完善了blockingqueue:

public sealed class BlockingQueue<T> : IDisposable
{
private readonly Queue<T> _queue = new Queue<T>(); private readonly object _lock = new object(); private readonly Semaphore _pool= new Semaphore(0, int.MaxValue); public void Enqueue(T item)
{
lock (_lock)
{
_queue.Enqueue(item);
}
} public T Dequeue()
{
_pool.WaitOne();
lock(_lock)
{
return _queue.Dequeue();
}
} public IEnumerable<T> ToArray()
{
return _queue.ToArray();
} public void Dispose()
{
_pool.Dispose();
}
}

简单写了一下自定义的线程池,上文中介绍到了Semaphore 这个信号量,下一节为Semaphore 的介绍和实现原理。

c# 异步进阶———— 自定义 taskschedule[三]的更多相关文章

  1. [.net 面向对象程序设计进阶] (4) 正则表达式 (三) 表达式助手

    [.net 面向对象程序设计进阶] (2) 正则表达式(三) 表达式助手 上面两节对正则表达式的使用及.NET下使用正则表达式作了详细说明,本节主要搜集整理了常用的正则表达式提供参考. 此外为了使用方 ...

  2. django 多对多自定义第三张表时的注意事项

    杂交(自定义第三张表+ManyToManyField) # modles.py class Boy(models.Model): name = models.CharField(max_length= ...

  3. [Android] Android 异步定时任务实现的三种方法(以SeekBar的进度自动实现为例)

    [Android] Android 定时异步任务实现的三种方法(以SeekBar的进度自动实现为例) 一.采用Handler与线程的sleep(long)方法 二.采用Handler与timer及Ti ...

  4. $Django 多对多-自定义第三张表 基于双下划线的跨表查询(补充)

    自定义第三张表的好处:可以定义多个字段, 缺点:查询不方便(有方法解决) 1.第三张表设置外键,联合唯一(查询不方便) class Books(models.Model): name=models.C ...

  5. Android自定义视图三:给自定义视图添加“流畅”的动画

    这个系列是老外写的,干货!翻译出来一起学习.如有不妥,不吝赐教! Android自定义视图一:扩展现有的视图,添加新的XML属性 Android自定义视图二:如何绘制内容 Android自定义视图三: ...

  6. javascript进阶课程--第三章--匿名函数和闭包

    javascript进阶课程--第三章--匿名函数和闭包 一.总结 二.学习要点 掌握匿名函数和闭包的应用 三.匿名函数和闭包 匿名函数 没有函数名字的函数 单独的匿名函数是无法运行和调用的 可以把匿 ...

  7. javascript进阶教程第三章--匿名和闭包--案例实战

    javascript进阶教程第三章--匿名和闭包--案例实战 一.学习任务 通过几个小练习回顾学过的知识点 二.实例 练习1: 实例描述:打开页面后规定时间内弹出一个新窗口,新窗口指定时间后自动关闭. ...

  8. iOS开发——UI进阶篇(三)自定义不等高cell,如何拿到cell的行高,自动计算cell高度,(有配图,无配图)微博案例

    一.纯代码自定义不等高cell 废话不多说,直接来看下面这个例子先来看下微博的最终效果 首先创建一个继承UITableViewController的控制器@interface ViewControll ...

  9. 自定义View(三)--实现一个简单地流式布局

    Android中的流式布局也就是常说的瀑布流很是常见,不仅在很多项目中都能见到,而且面试中也有很多面试官问道,那么什么是流式布局呢?简单来说就是如果当前行的剩余宽度不足以摆放下一个控件的时候,则自动将 ...

  10. 网易云课堂_C语言程序设计进阶_第三周:结构:结构、类型定义、联合

    3.1 枚举 3.2 结构 3.3 类型定义 3.1 枚举 枚举是一种用户定义的数据类型,它用关键字enum以如下语法来表明: enum 枚举类型名字{名字0,...,名字n}; 枚举类型名字通常并不 ...

随机推荐

  1. 翻下旧资料,发现96年考过foxbase二级

    翻下旧资料,找到 96年通过二级考试的证书,那时考的是Foxbase,一路走来,从最早用netware+dos无盘站+foxbase做订单系统,库存管理系统,再到使用记事本码asp网站,PB+orac ...

  2. 2020.4.28关于pta的总结

    0.前言 这次作业针对的是oo程序设计第二单元(5-8周)的作业,在整体当中属于第4到第6次作业. 此三次作业的知识要点为:继承类,多态性,字符串,结构类,接口类. 此三次作业的时间为20.4.12- ...

  3. 解决idea xml文件中的中文注释乱码

    今天用idea编译xml文件的时候报错了,打开编译好的xml文件发现是中文乱码问题,按照百度上靠前的办法设置了一下,并没有解决乱码问题,在当前项目中直接设置就是不生效,最后删掉编译好的 target/ ...

  4. websocket 数据分析

    WebSocket是一种通过单个TCP连接提供全双工通信信道的协议.2011年,IETF将WebSocket协议标准化为RFC 6455,W3C正在对Web IDL中的WebSocket API进行标 ...

  5. 实验二:Open vSwitch虚拟交换机实践

    基础要求提交 a) /home/用户名/学号/lab2/目录下执行ovs-vsctl show命令.以及p0和p1连通性测试的执行结果截图: b) /home/用户名/学号/lab2/目录下开启Min ...

  6. linux 查看进程的启动开始时间

    先使用命令查看需要查看的进程 ps -ef | grep java root 29861 13755 2 09:42 pts/0 00:10:48 java -jar XXXX.jar ps axo ...

  7. 密码破解-hashcat的简单使用

    在我们抓取到系统的hash值之后,需要通过一些工具来破解密码 hashcat是一款可以基于显卡暴力破解密码的工具,几乎支持了所有常见的加密,并且支持各种姿势的密码搭配 在kali Linux中自带的有 ...

  8. 对于如何在IDEA中给Terminal添加git的详解

    具体步骤 1.配置本机环境变量 进入到环境变量的设置界面,然后找到下面的Path变量,双击点开: 然后新建一个变量,路径定义到git的目录下面的bin目录下: 2.WIN+R,然后输入cmd,进入终端 ...

  9. 文件的上传&预览&下载学习(四)

    0.参考博客 https://blog.csdn.net/Chengzi_comm/article/details/53037967 逻辑清晰 https://blog.csdn.net/alli09 ...

  10. ReentrantLock 锁详解

    更多内容,前往个人博客 ReentrantLock 支持公平锁和非公平锁,可重入锁 ReentrantLock的底层是通过 AQS[链接]实现. 一.BAT 大厂的面试题 [1]什么是可重入,什么是可 ...