前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >react源码解析15.scheduler&Lane

react源码解析15.scheduler&Lane

原创
作者头像
zz1998
发布于 2021-12-06 01:15:06
发布于 2021-12-06 01:15:06
19700
代码可运行
举报
运行总次数:0
代码可运行

react源码解析15.scheduler&Lane

视频讲解(高效学习):进入学习
往期文章:

1.开篇介绍和面试题

2.react的设计理念

3.react源码架构

4.源码目录结构和调试

5.jsx&核心api

6.legacy和concurrent模式入口函数

7.Fiber架构

8.render阶段

9.diff算法

10.commit阶段

11.生命周期

12.状态更新流程

13.hooks源码

14.手写hooks

15.scheduler&Lane

16.concurrent模式

17.context

18事件系统

19.手写迷你版react

20.总结&第一章的面试题解答

21.demo

当我们在类似下面的搜索框组件进行搜索时会发现,组件分为搜索部分和搜索结果展示列表,我们期望输入框能立刻响应,结果列表可以有等待的时间,如果结果列表数据量很大,在进行渲染的时候,我们又输入了一些文字,因为用户输入事件的优先级是很高的,所以就要停止结果列表的渲染,这就引出了不同任务之间的优先级和调度

react源码15.5

Scheduler

我们知道如果我们的应用占用较长的js执行时间,比如超过了设备一帧的时间,那么设备的绘制就会出不的现象。

Scheduler主要的功能是时间切片和调度优先级,react在对比差异的时候会占用一定的js执行时间,Scheduler内部借助MessageChannel实现了在浏览器绘制之前指定一个时间片,如果react在指定时间内没对比完,Scheduler就会强制交出执行权给浏览器

react源码15.3

时间切片

​ 在浏览器的一帧中js的执行时间如下

react源码15.1

​ requestIdleCallback是在浏览器重绘重排之后,如果还有空闲就可以执行的时机,所以为了不影响重绘重排,可以在浏览器在requestIdleCallback中执行耗性能的计算,但是由于requestIdleCallback存在兼容和触发时机不稳定的问题,scheduler中采用MessageChannel来实现requestIdleCallback,当前环境不支持MessageChannel就采用setTimeout。

​ 在之前的介绍中我们知道在performUnitOfWork之后会执行render阶段和commit阶段,如果在浏览器的一帧中,cup的计算还没完成,就会让出js执行权给浏览器,这个判断在workLoopConcurrent函数中,shouldYield就是用来判断剩余的时间有没有用尽。在源码中每个时间片时5ms,这个值会根据设备的fps调整。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function workLoopConcurrent() {
  while (workInProgress !== null && !shouldYield()) {
    performUnitOfWork(workInProgress);
  }
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function forceFrameRate(fps) {//计算时间片
  if (fps < 0 || fps > 125) {
    console['error'](
      'forceFrameRate takes a positive int between 0 and 125, ' +
        'forcing frame rates higher than 125 fps is not supported',
    );
    return;
  }
  if (fps > 0) {
    yieldInterval = Math.floor(1000 / fps);
  } else {
    yieldInterval = 5;//时间片默认5ms
  }
}
任务的暂停

在shouldYield函数中有一段,所以可以知道,如果当前时间大于任务开始的时间+yieldInterval,就打断了任务的进行。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//deadline = currentTime + yieldInterval,deadline是在performWorkUntilDeadline函数中计算出来的
if (currentTime >= deadline) {
  //...
 return true
}
调度优先级

​ 在Scheduler中有两个函数可以创建具有优先级的任务

  • runWithPriority:以一个优先级执行callback,如果是同步的任务,优先级就是ImmediateSchedulerPriority function unstable_runWithPriority(priorityLevel, eventHandler) { switch (priorityLevel) {//5种优先级 case ImmediatePriority: case UserBlockingPriority: case NormalPriority: case LowPriority: case IdlePriority: break; default: priorityLevel = NormalPriority; } var previousPriorityLevel = currentPriorityLevel;//保存当前的优先级 currentPriorityLevel = priorityLevel;//priorityLevel赋值给currentPriorityLevel try { return eventHandler();//回调函数 } finally { currentPriorityLevel = previousPriorityLevel;//还原之前的优先级 } }
  • scheduleCallback:以一个优先级注册callback,在适当的时机执行,因为涉及过期时间的计算,所以scheduleCallback比runWithPriority的粒度更细。
    • 在scheduleCallback中优先级意味着过期时间,优先级越高priorityLevel就越小,过期时间离当前时间就越近,var expirationTime = startTime + timeout;例如IMMEDIATE_PRIORITY_TIMEOUT=-1,那var expirationTime = startTime + (-1);就小于当前时间了,所以要立即执行。
    • scheduleCallback调度的过程用到了小顶堆,所以我们可以在O(1)的复杂度找到优先级最高的task,不了解可以查阅资料,在源码中小顶堆存放着任务,每次peek都能取到离过期时间最近的task。
    • scheduleCallback中,未过期任务task存放在timerQueue中,过期任务存放在taskQueue中。 ​ 新建newTask任务之后,判断newTask是否过期,没过期就加入timerQueue中,如果此时taskQueue中还没有过期任务,timerQueue中离过期时间最近的task正好是newTask,则设置个定时器,到了过期时间就加入taskQueue中。 ​ 当timerQueue中有任务,就取出最早过期的任务执行。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function unstable_scheduleCallback(priorityLevel, callback, options) {
  var currentTime = getCurrentTime();

  var startTime;//开始时间
  if (typeof options === 'object' && options !== null) {
    var delay = options.delay;
    if (typeof delay === 'number' && delay > 0) {
      startTime = currentTime + delay;
    } else {
      startTime = currentTime;
    }
  } else {
    startTime = currentTime;
  }

  var timeout;
  switch (priorityLevel) {
    case ImmediatePriority://优先级越高timeout越小
      timeout = IMMEDIATE_PRIORITY_TIMEOUT;//-1
      break;
    case UserBlockingPriority:
      timeout = USER_BLOCKING_PRIORITY_TIMEOUT;//250
      break;
    case IdlePriority:
      timeout = IDLE_PRIORITY_TIMEOUT;
      break;
    case LowPriority:
      timeout = LOW_PRIORITY_TIMEOUT;
      break;
    case NormalPriority:
    default:
      timeout = NORMAL_PRIORITY_TIMEOUT;
      break;
  }

  var expirationTime = startTime + timeout;//优先级越高 过期时间越小

  var newTask = {//新建task
    id: taskIdCounter++,
    callback//回调函数
    priorityLevel,
    startTime,//开始时间
    expirationTime,//过期时间
    sortIndex: -1,
  };
  if (enableProfiling) {
    newTask.isQueued = false;
  }

  if (startTime > currentTime) {//没有过期
    newTask.sortIndex = startTime;
    push(timerQueue, newTask);//加入timerQueue
    //taskQueue中还没有过期任务,timerQueue中离过期时间最近的task正好是newTask
    if (peek(taskQueue) === null && newTask === peek(timerQueue)) {
      if (isHostTimeoutScheduled) {
        cancelHostTimeout();
      } else {
        isHostTimeoutScheduled = true;
      }
      //定时器,到了过期时间就加入taskQueue中
      requestHostTimeout(handleTimeout, startTime - currentTime);
    }
  } else {
    newTask.sortIndex = expirationTime;
    push(taskQueue, newTask);//加入taskQueue
    if (enableProfiling) {
      markTaskStart(newTask, currentTime);
      newTask.isQueued = true;
    }
    if (!isHostCallbackScheduled && !isPerformingWork) {
      isHostCallbackScheduled = true;
      requestHostCallback(flushWork);//执行过期的任务
    }
  }

  return newTask;
}

react源码15.2

任务暂停之后怎么继续

​ 在workLoop函数中有这样一段

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const continuationCallback = callback(didUserCallbackTimeout);//callback就是调度的callback
currentTime = getCurrentTime();
if (typeof continuationCallback === 'function') {//判断callback执行之后的返回值类型
  currentTask.callback = continuationCallback;//如果是function类型就把又赋值给currentTask.callback
  markTaskYield(currentTask, currentTime);
} else {
  if (enableProfiling) {
    markTaskCompleted(currentTask, currentTime);
    currentTask.isQueued = false;
  }
  if (currentTask === peek(taskQueue)) {
    pop(taskQueue);//如果是function类型就从taskQueue中删除
  }
}
advanceTimers(currentTime);

​ 在performConcurrentWorkOnRoot函数的结尾有这样一个判断,如果callbackNode等于originalCallbackNode那就恢复任务的执行

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (root.callbackNode === originalCallbackNode) {
  // The task node scheduled for this root is the same one that's
  // currently executed. Need to return a continuation.
  return performConcurrentWorkOnRoot.bind(null, root);
}

Lane

​ Lane的和Scheduler是两套优先级机制,相比来说Lane的优先级粒度更细,Lane的意思是车道,类似赛车一样,在task获取优先级时,总是会优先抢内圈的赛道,Lane表示的优先级有以下几个特点。

  • 可以表示不同批次的优先级 ​ 从代码中中可以看到,每个优先级都是个31位二进制数字,1表示该位置可以用,0代表这个位置不能用,从第一个优先级NoLanes到OffscreenLane优先级是降低的,优先级越低1的个数也就越多(赛车比赛外圈的车越多),也就是说含多个1的优先级就是同一个批次。 export const NoLanes: Lanes = /* */ 0b0000000000000000000000000000000; export const NoLane: Lane = /* */ 0b0000000000000000000000000000000; export const SyncLane: Lane = /* */ 0b0000000000000000000000000000001; export const SyncBatchedLane: Lane = /* */ 0b0000000000000000000000000000010; export const InputDiscreteHydrationLane: Lane = /* */ 0b0000000000000000000000000000100; const InputDiscreteLanes: Lanes = /* */ 0b0000000000000000000000000011000; const InputContinuousHydrationLane: Lane = /* */ 0b0000000000000000000000000100000; const InputContinuousLanes: Lanes = /* */ 0b0000000000000000000000011000000; export const DefaultHydrationLane: Lane = /* */ 0b0000000000000000000000100000000; export const DefaultLanes: Lanes = /* */ 0b0000000000000000000111000000000; const TransitionHydrationLane: Lane = /* */ 0b0000000000000000001000000000000; const TransitionLanes: Lanes = /* */ 0b0000000001111111110000000000000; const RetryLanes: Lanes = /* */ 0b0000011110000000000000000000000; export const SomeRetryLane: Lanes = /* */ 0b0000010000000000000000000000000; export const SelectiveHydrationLane: Lane = /* */ 0b0000100000000000000000000000000; const NonIdleLanes = /* */ 0b0000111111111111111111111111111; export const IdleHydrationLane: Lane = /* */ 0b0001000000000000000000000000000; const IdleLanes: Lanes = /* */ 0b0110000000000000000000000000000; export const OffscreenLane: Lane = /* */ 0b1000000000000000000000000000000;
  • 优先级的计算的性能高 ​ 例如,可以通过二进制按位与来判断a和b代表的lane是否存在交集 export function includesSomeLane(a: Lanes | Lane, b: Lanes | Lane) { return (a & b) !== NoLanes; }
Lane模型中task是怎么获取优先级的(赛车的初始赛道)

​ 任务获取赛道的方式是从高优先级的lanes开始的,这个过程发生在findUpdateLane函数中,如果高优先级没有可用的lane了就下降到优先级低的lanes中寻找,其中pickArbitraryLane会调用getHighestPriorityLane获取一批lanes中优先级最高的那一位,也就是通过lanes & -lanes获取最右边的一位

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
export function findUpdateLane(
  lanePriority: LanePriority,
  wipLanes: Lanes,
): Lane {
  switch (lanePriority) {
    //...
    case DefaultLanePriority: {
      let lane = pickArbitraryLane(DefaultLanes & ~wipLanes);//找到下一个优先级最高的lane
      if (lane === NoLane) {//上一个level的lane都占满了下降到TransitionLanes继续寻找可用的赛道
        lane = pickArbitraryLane(TransitionLanes & ~wipLanes);
        if (lane === NoLane) {//TransitionLanes也满了
          lane = pickArbitraryLane(DefaultLanes);//从DefaultLanes开始找
        }
      }
      return lane;
    }
  }
}
Lane模型中高优先级是怎么插队的(赛车抢赛道)

​ 在Lane模型中如果一个低优先级的任务执行,并且还在调度的时候触发了一个高优先级的任务,则高优先级的任务打断低优先级任务,此时应该先取消低优先级的任务,因为此时低优先级的任务可能已经进行了一段时间,Fiber树已经构建了一部分,所以需要将Fiber树还原,这个过程发生在函数prepareFreshStack中,在这个函数中会初始化已经构建的Fiber树

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
  const existingCallbackNode = root.callbackNode;//之前已经调用过的setState的回调
  //...
 if (existingCallbackNode !== null) {
    const existingCallbackPriority = root.callbackPriority;
    //新的setState的回调和之前setState的回调优先级相等 则进入batchedUpdate的逻辑
    if (existingCallbackPriority === newCallbackPriority) {
      return;
    }
    //两个回调优先级不一致,则被高优先级任务打断,先取消当前低优先级的任务
    cancelCallback(existingCallbackNode);
  }
 //调度render阶段的起点
 newCallbackNode = scheduleCallback(
    schedulerPriorityLevel,
    performConcurrentWorkOnRoot.bind(null, root),
  );
 //...
}

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function prepareFreshStack(root: FiberRoot, lanes: Lanes) {
  root.finishedWork = null;
  root.finishedLanes = NoLanes;
 //...
  //workInProgressRoot等变量重新赋值和初始化
  workInProgressRoot = root;
  workInProgress = createWorkInProgress(root.current, null);
  workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;
  workInProgressRootExitStatus = RootIncomplete;
  workInProgressRootFatalError = null;
  workInProgressRootSkippedLanes = NoLanes;
  workInProgressRootUpdatedLanes = NoLanes;
  workInProgressRootPingedLanes = NoLanes;
 //...
}
Lane模型中怎么解决饥饿问题(最后一名赛车最后也要到达终点啊)

​ 在调度优先级的过程中,会调用markStarvedLanesAsExpired遍历pendingLanes(未执行的任务包含的lane),如果没过期时间就计算一个过期时间,如果过期了就加入root.expiredLanes中,然后在下次调用getNextLane函数的时候会优先返回expiredLanes

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
export function markStarvedLanesAsExpired(
  root: FiberRoot,
  currentTime: number,
): void {

  const pendingLanes = root.pendingLanes;
  const suspendedLanes = root.suspendedLanes;
  const pingedLanes = root.pingedLanes;
  const expirationTimes = root.expirationTimes;

  let lanes = pendingLanes;
  while (lanes > 0) {//遍历lanes
    const index = pickArbitraryLaneIndex(lanes);
    const lane = 1 << index;

    const expirationTime = expirationTimes[index];
    if (expirationTime === NoTimestamp) {

      if (
        (lane & suspendedLanes) === NoLanes ||
        (lane & pingedLanes) !== NoLanes
      ) {
        expirationTimes[index] = computeExpirationTime(lane, currentTime);//计算过期时间
      }
    } else if (expirationTime <= currentTime) {//过期了
      root.expiredLanes |= lane;//在expiredLanes加入当前遍历到的lane
    }

    lanes &= ~lane;
  }
}

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes {
  //...
  if (expiredLanes !== NoLanes) {
    nextLanes = expiredLanes;
    nextLanePriority = return_highestLanePriority = SyncLanePriority;//优先返回过期的lane
  } else {
  //...
    }
  return nextLanes;
}

​ 下图更直观,随之时间的推移,低优先级的任务被插队,最后也会变成高优先级的任务

react源码15.4

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
多线程-概述及底层实现机制浅析
在打算写这篇多线层底层实现机制的时候,突然发现自己对于计算机竟然懂得这么表面,对于CPU的工作原理都不完全清楚,于是又转头查看了一些CPU相关的资料。也不敢钻的太深,怕自己迷路...,其中如有错误,望知道的朋友在下面留言评论,我会及时更新的。
進无尽
2018/09/12
1.2K0
多线程-概述及底层实现机制浅析
什么叫应用程序域?(zhuan)
“域”,就是范围,环境,边界的意思,那么什么是应用程序域,官方给出的是这样的解释:操作系统和运行库环境通常会
全栈程序员站长
2022/09/07
4020
.Net托管世界的应用程序域和线程-网摘
.Net框架提供了全新的计算平台,给出了一致性的面向对象的编程环境,解决了安全、版本控制等原来系统平台中存在的问题,通过 公用语言运行库(CLR)提供了一个高效、安全的程序执行环境,也就是托管(也称作受控,Managed)环境。在这个类似虚拟机环境下,我们编写的程序 是如何运行、如何“托管”的呢?这个托管的世界如何同非托管的世界相互联系呢?
jack.yang
2025/04/05
870
C#基础知识学习之 ☀️ | 多线程的使用基础
线程在程序中经常被用到,现在的计算机都是可以异步执行很多操作的,所以多线程的作用可见一斑!
呆呆敲代码的小Y
2021/10/29
7920
C#基础知识学习之 ☀️ | 多线程的使用基础
并发编程之进程与线程
单核CPU下,线程实际还是串行执行的。操作系统中有一个组件叫做任务调度器,将CPU的时间片(windows下时间片最小约为15毫秒)分给不同的线程使用,只是由于CPU在线程间(时间片很短)的切换非常快,人类感觉是同时运行的。总结为一句话就是 :微观串行,宏观并行。 一般会将这种线程轮流使用CPU的做法称为并发,concurrent
海仔
2020/02/18
3800
并发编程之进程与线程
快速入门系列--CLR--02多线程
最近,由于基础框架的整体升级,因此需要更新所有相关项目的DLL文件。这个过程存在不小的风险,因此也对发布后的生产服务器进行了密切的监控,结果还是出现了个别应用出现异常的情况,很快的占用了大量的服务器内存和CPU等资源。通过研究dump,初步发现是由于配置服务器出现单点故障,然后应用通过多线程调用相关SOA服务时出现异常,引发了ThreadAbortException异常,而且由于原有异常处理代码不够严谨,而且与异步发送报警邮件紧密结合在一起,造成线程数量的几何级增加,最终使得整个服务器不可用。这儿介绍的不算
用户1216676
2018/01/24
9340
快速入门系列--CLR--02多线程
JUC 多线程01--线程、进程概念
正在进行中的程序。 每一个进程至少有一个线程。当程序运行时在内存空间中开辟一片独立空间。每一个进程都有一个执行顺序。 一个进程更象一个任务。 进程的内存原理:
潇洒
2023/10/20
1390
JUC 多线程01--线程、进程概念
java多线程系列_线程简介(1)
    线程是程序运行的基本执行单元。当操作系统(不包括单线程的操作系统,如微软早期的DOS)在执行一个程序时,会在系统中建立一个进程,而在这个进程中,必须至少建立一个线程(这个线程被称为主线程)来作为这个程序运行的入口点。因此,在操作系统中运行的任何程序都至少有一个主线程。
Hongten
2018/09/13
5930
.Net 中各种线程同步锁
编程编的久了,总会遇到多线程的情况,有些时候我们要几个线程合作完成某些功能,这时候可以定义一个全局对象,各个线程根据这个对象的状态来协同工作,这就是基本的线程同步。
tuoxie
2024/08/18
1740
.Net 中各种线程同步锁
多线程与多进程
操作系统比如 Mac OS X,Linux,Windows 等,都是支持“多任务”的操作系统,操作系统可以同时运行多个任务。一边在逛淘宝,一边在听音乐,一边在用微信聊天,这就是多任务,至少同时有 3 个任务正在运行。
@小森
2024/03/15
1000
多线程与多进程
.NET简谈组件程序设计之(AppDomain应用程序域)
最近在苦学.NET底层框架模型,发现.NET深入真的不是一般的难,不开源、没有相关系统的官方的书籍做学习资料,只能零散的看MSDN。要想摸熟.NET的模型真的并非易事。慢慢来吧。[王清培版权所有,转载请给出署名]
王清培
2022/03/14
3150
.NET简谈组件程序设计之(AppDomain应用程序域)
.NET简谈组件程序设计之(初识远程调用)
在.NET1.0版本出来的时候,要想进行远程调用基本上都是通过WebService的方式。而随着.NET2.0版本的出现,我们可以通过一个更加方便且高扩展性的框架来进行编写远程调用的程序,也就是我们都比较熟悉的.NetRemoting。
王清培
2022/03/14
3060
.NET简谈组件程序设计之(初识远程调用)
18 Python 基础: 重点知识点--进程和线程讲解
本文首发于腾讯云+社区,也可关注微信公众号【离不开的网】支持一下,就差你的关注支持了。
野原测试开发
2019/07/24
7410
18 Python 基础: 重点知识点--进程和线程讲解
进程和线程的区别
进程和线程的区别 简而言之,一个程序至少有一个进程,一个进程至少有一个线程. 线程的划分尺度小于进程,使得多线程程序的并发性高。 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做
猿人谷
2018/01/17
1.5K0
进程和线程的区别
.NET 面试题汇总(带答案)
答:尽可能用约束(包括CHECK、主键、唯一键、外键、非空字段)实现,这种方式的效率最好;其次用触发器,这种方式可以保证无论何种业务系统访问数据库都能维持数据库的完整性、一致性;最后再考虑用自写业务逻辑实现,但这种方式效率最低、编程最复杂,当为下下之策。
庞小明
2021/07/08
1.3K0
Enterprise Library 4.0缓存应用程序块
英文原文:http://msdn.microsoft.com/zh-cn/library/cc511588(en-us).aspx Enterprise Library 缓存应用程序块允许开发人员在应用程序中合并一个局部缓存,它支持内存内的缓存,和可选的可以是数据库存储或独立存储的后端存储。应用程序块可以不做修改的使用,它提供所有必须的获取、添加和移除缓存数据的功能。可配置的到期和清除策略也是应用程序块的一部分。 在构建企业范围发布的应用程序时,架构和开发人员都要面对许多挑战,缓存可以帮助他们战胜其中的包
张善友
2018/01/19
1K0
线程、进程、多线程、多进程、多任务,傻傻分不清?
进程是程序执行时的一个实例,即它是程序已经执行到课中程度的数据结构的汇集。从内核的观点看,进程的目的就是担当分配系统资源(CPU时间、内存等)的基本单位。
PHP开发工程师
2021/05/14
4840
线程、进程、多线程、多进程、多任务,傻傻分不清?
Golang中的协程(goroutine)
        进程就是程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单位,进程是一个动态概念,是程序在执行过程中分配和管理资源的基本单位,每一个进程都有一个自己的地址空间。一个进程至少有5种基本状态:初始状态,执行状态,等待状态,就绪状态,终止状态。通俗的讲,进程就是一个正在执行的程序。
周小末天天开心
2023/10/16
6990
解析.NET对象的跨应用程序域访问(上篇)
彭泽0902
2018/01/04
1.1K0
解析.NET对象的跨应用程序域访问(上篇)
C# Assembly
在C#中,Assembly是.NET框架的一个基本构建模块。它可以被看作是一个包含代码和资源的可部署单元,通常以DLL或EXE文件的形式存在。Assembly承载了以下几个关键特性:
JusterZhu
2025/01/23
1270
C# Assembly
相关推荐
多线程-概述及底层实现机制浅析
更多 >
LV.1
这个人很懒,什么都没有留下~
目录
  • 视频讲解(高效学习):进入学习
  • 往期文章:
  • Scheduler
  • 时间切片
  • 任务的暂停
  • 调度优先级
  • 任务暂停之后怎么继续
  • Lane
    • Lane模型中task是怎么获取优先级的(赛车的初始赛道)
    • Lane模型中高优先级是怎么插队的(赛车抢赛道)
    • Lane模型中怎么解决饥饿问题(最后一名赛车最后也要到达终点啊)
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档