什么是?

xt是对“调度程序()”的通用抽象。个别框架会有自己的抽象调度程序,比如..Tasks。当Tasks通过委托的形式进行排队和执行时,会用到..Tasks.。和xt提供了一个 Post方法用于将委托排队调用一样(稍后,我们会通过典型的委托调用机制来调用委托),也提供了一个 方法(稍后,我们会通过方法来调用该Task)。

通过.我们可以获取到Task默认的调度程序ler——线程池(译注:这下知道为什么Task默认使用的是线程池线程了吧)。并且可以通过继承来重写相关方法来实现在任意时间任意地点进行Task调用。例如,核心库中有个类,名为..Tasks.,其实例公开了两个属性,一个叫,另一个叫。调度给的任务可以并发,但是要在构造时就要指定最大并发数(类似于前面演示的);相反,在执行任务时,那么将只允许运行一个排他任务,这个行为很像读写锁。

和xt一样,也有一个属性,会返回当前调度程序。不过,和xt不同的是,它没有设置当前调度程序的方法,而是在启动Task时就要提供,因为当前调度程序是与当前运行的Task相关联的。所以,下方的示例程序会输出“True”,这是因为和一起使用的表达式是在的上执行的(我们手动指定cesp.),并且.也

using System;using System.Threading.Tasks;
class Program{ static void Main() { var cesp = new ConcurrentExclusiveSchedulerPair(); Task.Factory.StartNew(() => { Console.WriteLine(TaskScheduler.Current == cesp.ExclusiveScheduler); }, default, TaskCreationOptions.None, cesp.ExclusiveScheduler) .Wait(); }}

任务调度器的原理

taskctl_c#task_taskchanger下载

public abstract class TaskScheduler{    // 任务入口,待调度执行的 Task 会通过该方法传入,调度器会将任务安排task到指定的队列(线程池任务队列(全局任务队列、本地队列)、独立线程、ui线程) 只能被.NET Framework调用,不能配派生类调用   //    protected internal abstract void QueueTask(Task task);
// 这个是在执行 Task 回调的时候才会被执行到的方法,放到后面再讲 protected abstract bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued); protected abstract bool TryExecuteTask(Task task, bool taskWasPreviouslyQueued);// 获取所有调度到该 TaskScheduler 的 Task protected abstract IEnumerable? GetScheduledTasks(); }

.net中的任务调度器有哪些

线程池任务调度器:ler、

核心库任务调度器:

UI任务调度器:,并发度为1

平时我们在用多线程开发的时候少不了Task,确实task给我们带来了巨大的编程效率,在Task底层有一个,它决定了task该如何被调度,而在.net 中有两种系统定义,第一个是Task默认的ler,还是一种就是(wpf),默认的调度器无法控制任务优先级,那么需要自定义调度器实现优先级控制。

以及这两种类型之外的如何自定义,这篇刚好和大家分享一下。

一: ler

taskchanger下载_taskctl_c#task

这种机制是task的默认机制,而且从名字上也可以看到它是一种委托到的机制,刚好也从侧面说明task是基于基础上的封装,源代码(#../ler.cs,)

ler的原理:将指定的长任务开辟一个独立的线程去执行,未指定的长时间运行的任务就用线程池的线程执行

internal sealed class ThreadPoolTaskScheduler : TaskScheduler    {//其他代码   protected internal override void QueueTask(Task task)        {            TaskCreationOptions options = task.Options;            if (Thread.IsThreadStartSupported && (options & TaskCreationOptions.LongRunning) != 0)            {                // Run LongRunning tasks on their own dedicated thread.                new Thread(s_longRunningThreadWork)                {                    IsBackground = true,                    Name = ".NET Long Running Task"                }.UnsafeStart(task);            }            else            {                // Normal handling for non-LongRunning tasks.                ThreadPool.UnsafeQueueUserWorkItemInternal(task, (options & TaskCreationOptions.PreferFairness) == 0);            }        }//其他代码  }

二:

使用条件:只有当前程的同步上下文不为null时,该方法才能正常使用。例如在UI线程(wpf、 、 )中,UI线程的同步上下文不为Null。控制台默认的当前线程同步上下文为null,如果给当前线程设置默认的同步上下文xt.ntext(new xt());就可以正常使用该方法。如果控制台程序的线程未设置同步上下将引发【当前的 xt 不能用作 】异常。

默认的同步上下文将方法委托给线程池执行。

使用方式:通过.() 调用。

原理:初始化时候捕获当前的线程的同步上下文。将同步上下文封装入任务调度器形成新的任务调度器。重写该任务调度器中的方法c#task,利用同步上下文的post方法将任务送到不同的处理程序,如果是的UI线程同步上下文 的post方法(已重写post方法),就将任务送到UI线程。如果是控制台线程(默认为null 设置默认同步上下文后可以正常使用。默认同步上下文采用线程池线程)就将任务送入线程池处理。

在中的同步上下文:

在wpf中的同步上下文:

在控制台线程池new 同步上下文:都默认为Null。可以给他们设置默认的同步上下文xt。xt.ntext(new xt());

xt 综述 | Docs

()

以下是部分源代码

internal sealed class SynchronizationContextTaskScheduler : TaskScheduler    {
//初始化时候 ,捕获当前线程的同步上下文 internal SynchronizationContextTaskScheduler() { m_synchronizationContext = SynchronizationContext.Current ?? // make sure we have a synccontext to work with throw new InvalidOperationException(SR.TaskScheduler_FromCurrentSynchronizationContext_NoCurrent); }
//其他代码private readonly SynchronizationContext m_synchronizationContext;protected internal override void QueueTask(Task task) { m_synchronizationContext.Post(s_postCallback, (object)task); }//其他代码///改变post的调度方法、 调用者线程执行各方面的任务操作
private static readonly SendOrPostCallback s_postCallback = static s => { Debug.Assert(s is Task); ((Task)s).ExecuteEntry(); //调用者线程执行各方面的任务操作 }; }

网站源码:

#../.cs,

以下是xt部分源代码

public partial class SynchronizationContext    {    //其他代码    public virtual void Post(SendOrPostCallback d, object? state) => ThreadPool.QueueUserWorkItem(static s => s.d(s.state), (d, state), preferLocal: false);   //其他代码  }

网站源代码

(#../xt.cs,)

有了这个基础我们再来看一下代码怎么写,可以看到,下面这段代码是不阻塞的,完美~~~

private void button1_Click(object sender, EventArgs e)         {             Task task = Task.Factory.StartNew(() =>             {                 //复杂操作,等待10s                 Thread.Sleep(10000);
}).ContinueWith((t) => { button1.Text = "hello world"; }, TaskScheduler.FromCurrentSynchronizationContext()); }

三:自定义

我们知道在现有的.net 中只有这么两种,有些同学可能想问,这些我用起来不爽,我想自定义一下,这个可以吗?当然!!!如果你想自定义,只要自定义一个类实现一下就可以了,然后你可以将ler简化一下,即我要求所有的Task都需要走,杜绝使用,这样可以吗,当然了,不信你看。

namespace ConsoleApplication1{    class Program    {        static void Main(string[] args)        {            var task = Task.Factory.StartNew(() =>            {                Console.WriteLine("hello world!!!");            }, new CancellationToken(), TaskCreationOptions.None, new PerThreadTaskScheduler());
Console.Read(); } }
/// /// 每个Task一个Thread /// public class PerThreadTaskScheduler : TaskScheduler { protected override IEnumerable GetScheduledTasks() { return null; }
protected override void QueueTask(Task task) { var thread = new Thread(() => { TryExecuteTask(task); });
thread.Start(); }
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) { throw new NotImplementedException(); } }}

方法的使用

.() 方法

创建一个与当前xt关联的。源代码如下:

假设有一个UI App,它有一个按钮。当点击按钮后,会从网上下载一些文本并将其设置为按钮的内容。我们应当只在UI线程中访问该按钮,因此当我们成功下载新的文本后,我们需要从拥有按钮控制权的的线程中将其设置为按钮的内容。如果不这样做的话,会得到一个这样的异常:

System.InvalidOperationException: 'The calling thread cannot access this object because a different thread owns it.'

如果我们自己手动实现,那么可以使用前面所述的xt将按钮内容的设置传回原始上下文,例如借助:

用法如下

private static readonly HttpClient s_httpClient = new HttpClient();
private void downloadBtn_Click(object sender, RoutedEventArgs e){ s_httpClient.GetStringAsync("http://example.com/currenttime").ContinueWith(downloadTask => { downloadBtn.Content = downloadTask.Result; }, TaskScheduler.FromCurrentSynchronizationContext());//捕获当前UI线程的同步上下文}

会员全站资源免费获取,点击查看会员权益

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注