JIT(Just-In-Time)编译器

在 .NET 中,JIT(Just-In-Time)编译器是一种将 MSIL(Microsoft Intermediate Language)代码转换为本机机器代码的动态编译器。 JIT 编译器在程序运行时将 MSIL 代码编译为本机代码,以便在计算机上运行。 JIT 编译器是 .NET 运行时的一部分,它负责将 MSIL 代码转换为本机代码。 当 .NET 应用程序启动时,CLR(Common Language Runtime)会将 MSIL 代码加载到内存中, 然后 JIT 编译器会将代码编译为本机代码,以便在计算机上运行。这个过程是在运行时动态进行的,因此称之为 JIT。 JIT 编译器的好处是它可以根据当前计算机的硬件和操作系统环境来优化代码, 以提高应用程序的性能。它还可以在运行时检查代码中的错误,以避免运行时错误。 JIT 编译器还可以根据代码的执行情况进行优化,以提高应用程序的性能。 总之,JIT 编译器是 .NET 运行时的重要组成部分,它可以将 MSIL 代码转换为本机代码, 以便在计算机上运行,并且可以根据当前环境进行优化,以提高应用程序的性能。

2025年7月28日 · 1 分钟 · 39 字

JWT

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各方之间安全地传输信息。以下是关于JWT的关键信息: JWT基本概念 定义:JWT是一种基于JSON的开放标准,用于在各方之间安全地传输声明 用途:常用于身份验证和信息交换 结构:由三部分组成,用点(.)分隔 JWT的三个组成部分 Header(头部) 包含令牌类型和签名算法信息 例如:{"alg": "HS256", "typ": "JWT"} Payload(载荷) 包含实际要传输的数据(声明) 可以包含标准声明(如iss、exp、sub等)或自定义声明 Signature(签名) 用于验证令牌的完整性 通过签名算法对前两部分进行签名生成 主要特点 无状态:服务端不需要存储会话信息 跨域支持:可以在不同域之间传递 紧凑性:体积小,适合在URL、POST参数或HTTP头中传输 自包含:载荷中包含用户所需的所有信息 常见用途 身份验证令牌 单点登录(SSO) API认证 信息交换 JWT广泛应用于现代Web应用和API安全认证中。 起源 需要了解一门技术,首先从为什么产生开始说起是最好的。JWT主要用于用户登录鉴权,所以我们从最传统的session认证开始说起。 session认证 众所周知,http协议本身是无状态的协议,那就意味着当有用户向系统使用账户名称和密码进行用户认证之后,下一次请求还要再一次用户认证才行。因为我们不能通过http协议知道是哪个用户发出的请求,所以如果要知道是哪个用户发出的请求,那就需要在服务器保存一份用户信息(保存至session),然后在认证成功后返回cookie值传递给浏览器,那么用户在下一次请求时就可以带上cookie值,服务器就可以识别是哪个用户发送的请求,是否已认证,是否登录过期等等。这就是传统的session认证方式。session认证的缺点其实很明显,由于session是保存在服务器里,所以如果分布式部署应用的话,会出现session不能共享的问题,很难扩展。于是乎为了解决session共享的问题,又引入了redis,接着往下看。 token认证 这种方式跟session的方式流程差不多,不同的地方在于保存的是一个token值到redis,token一般是一串随机的字符(比如UUID),value一般是用户ID,并且设置一个过期时间。每次请求服务的时候带上token在请求头,后端接收到token则根据token查一下redis是否存在,如果存在则表示用户已认证,如果token不存在则跳到登录界面让用户重新登录,登录成功后返回一个token值给客户端。优点是多台服务器都是使用redis来存取token,不存在不共享的问题,所以容易扩展。缺点是每次请求都需要查一下redis,会造成redis的压力,还有增加了请求的耗时,每个已登录的用户都要保存一个token在redis,也会消耗redis的存储空间。有没有更好的方式呢?接着往下看。 什么是JWT JWT(全称:Json Web Token)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。上面说法比较文绉绉,简单点说就是一种认证机制,让后台知道该请求是来自于受信的客户端。 流程描述一下: 用户使用账号、密码登录应用,登录的请求发送到Authentication Server。 Authentication Server进行用户验证,然后创建JWT字符串返回给客户端。 客户端请求接口时,在请求头带上JWT。 Application Server验证JWT合法性,如果合法则继续调用应用接口返回结果。可以看出与token方式有一些不同的地方,就是不需要依赖redis,用户信息存储在客户端。所以关键在于生成JWT,和解析JWT这两个地方。 JWT的数据结构 JWT一般是这样一个字符串,分为三个部分,以".“隔开:xxxxx.yyyyy.zzzzz Header JWT第一部分是头部分,它是一个描述JWT元数据的Json对象,通常如下所示。 { "alg": "HS256", "typ": "JWT" } alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256),typ属性表示令牌的类型,JWT令牌统一写为JWT。 最后,使用Base64 URL算法将上述JSON对象转换为字符串保存。 Payload JWT第二部分是Payload,也是一个Json对象,除了包含需要传递的数据,还有七个默认的字段供选择。 分别是,iss:发行人、exp:到期时间、sub:主题、aud:用户、nbf:在此之前不可用、iat:发布时间、jti:JWT ID用于标识该JWT。 如果自定义字段,可以这样定义: { //默认字段 "sub":"主题123", //自定义字段 "name":"java技术爱好者", "isAdmin":"true", "loginTime":"2021-12-05 12:00:03" } 需要注意的是,默认情况下JWT是未加密的,任何人都可以解读其内容,因此如果一些敏感信息不要存放在此,以防信息泄露。 JSON对象也使用Base64 URL算法转换为字符串保存。 Signature JWT第三部分是签名。是这样生成的,首先需要指定一个secret,该secret仅仅保存在服务器中,保证不能让其他用户知道。 然后使用Header指定的算法对Header和Payload进行计算,然后就得出一个签名哈希。也就是Signature。 那么Application Server如何进行验证呢?可以利用JWT前两段,用同一套哈希算法和同一个secret计算一个签名值,然后把计算出来的签名值和收到的JWT第三段比较,如果相同则认证通过。 JWT的优点 json格式的通用性,所以JWT可以跨语言支持,比如Java、JavaScript、PHP、Node等等。 可以利用Payload存储一些非敏感的信息。 便于传输,JWT结构简单,字节占用小。 不需要在服务端保存会话信息,易于应用的扩展。 总结 最后讲讲JWT的缺点,任何技术都不是完美的,所以我们得用辩证思维去看待任何一项技术。 安全性没法保证,所以jwt里不能存储敏感数据。因为jwt的payload并没有加密,只是用Base64编码而已。 无法中途废弃。因为一旦签发了一个jwt,在到期之前始终都是有效的,如果用户信息发生更新了,只能等旧的jwt过期后重新签发新的jwt。 续签问题。当签发的jwt保存在客户端,客户端一直在操作页面,按道理应该一直为客户端续长有效时间,否则当jwt有效期到了就会导致用户需要重新登录。那么怎么为jwt续签呢?最简单粗暴就是每次签发新的jwt,但是由于过于暴力,会影响性能。如果要优雅一点,又要引入Redis解决,但是这又把无状态的jwt硬生生变成了有状态的,违背了初衷。 所以印证了那句话,没有最好的技术,只有适合的技术。 // 好的,以下是一个使用 C# 语言编写的 JWT 示例程序,您可以参考它来编写您自己的 JWT 程序: using System; using System.Text; using System.Security.Claims; using Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; class Program { static void Main(string[] args) { // 定义 JWT 的签名密钥 var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("my_secret_key")); // 定义 JWT 的签名算法 var signingCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256); // 定义 JWT 的负载 var claims = new[] { new Claim(JwtRegisteredClaimNames.Sub, "1234567890"), new Claim(JwtRegisteredClaimNames.NameId, "John Smith"), new Claim(JwtRegisteredClaimNames.Email, "john.smith@example.com"), new Claim(JwtRegisteredClaimNames.Iat, DateTime.Now.ToUniversalTime().ToString(), ClaimValueTypes.Integer64) }; // 创建 JWT 的描述信息 var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(claims), Expires = DateTime.UtcNow.AddDays(7), SigningCredentials = signingCredentials }; // 创建 JWT var tokenHandler = new JwtSecurityTokenHandler(); var token = tokenHandler.CreateToken(tokenDescriptor); // 输出 JWT Console.WriteLine(tokenHandler.WriteToken(token)); } } // 在上面的示例程序中,我们首先定义了 JWT 的签名密钥和签名算法。然后我们定义了 JWT 的负载,即包含了一些标准声明和自定义声明。接着我们创建了 JWT 的描述信息,并设置了 JWT 的过期时间和签名凭证。最后我们通过 JwtSecurityTokenHandler 类的 CreateToken 方法创建了 JWT,并通过 WriteToken 方法将 JWT 输出到控制台。 // 希望这个示例程序能够帮助您编写您自己的 JWT 程序! using System; using System.Text; using System.Security.Cryptography; class Program { static void Main(string[] args) { // 1. 设置 JWT 的 header 和 payload var header = "{\"alg\":\"HS256\",\"typ\":\"JWT\"}"; var payload = "{\"sub\":\"1234567890\",\"name\":\"John Doe\",\"iat\":1516239022}"; // 2. 对 header 和 payload 进行 base64 编码 var encodedHeader = Base64UrlEncode(Encoding.UTF8.GetBytes(header)); var encodedPayload = Base64UrlEncode(Encoding.UTF8.GetBytes(payload)); // 3. 使用 HMACSHA256 算法生成签名 var key = Encoding.UTF8.GetBytes("your-256-bit-secret"); var hmac = new HMACSHA256(key); var signature = hmac.ComputeHash(Encoding.UTF8.GetBytes($"{encodedHeader}.{encodedPayload}")); // 4. 对签名进行 base64 编码 var encodedSignature = Base64UrlEncode(signature); // 5. 拼接 JWT var jwt = $"{encodedHeader}.{encodedPayload}.{encodedSignature}"; // 6. 输出 JWT Console.WriteLine(jwt); } static string Base64UrlEncode(byte[] input) { var output = Convert.ToBase64String(input); output = output.Replace("+", "-").Replace("/", "_").Replace("=", ""); return output; } } OAuth 在学习OAuth时,会有很多疑问OAuth到底是什么,本文介绍OAuth工作机制. ...

2025年7月28日 · 2 分钟 · 357 字

.NET异步方法

Task.run和threadpool.QueueUserWorkItem Task.Run 和 ThreadPool.QueueUserWorkItem 都是在 .NET 中用于执行异步任务的方法,但它们之间有一些区别。 返回值类型不同:Task.Run 返回一个 Task 对象,而 ThreadPool.QueueUserWorkItem 没有返回值。 参数不同:Task.Run 接受一个 Func<Task> 或 Action 委托,而 ThreadPool.QueueUserWorkItem 接受一个 WaitCallback 委托。 线程池的使用方式不同:Task.Run 会使用线程池中的线程来执行任务,而 ThreadPool.QueueUserWorkItem 只能使用线程池中的线程来执行任务。 异常处理不同:Task.Run 可以通过 await 关键字或 Task.Wait 方法等方式来捕获任务中的异常,而 ThreadPool.QueueUserWorkItem 没有提供类似的机制,需要在任务中自行处理异常。 综上所述,Task.Run 更加灵活,可以更好地支持异步编程模式,而 ThreadPool.QueueUserWorkItem 更适合执行简单的后台任务。在一般情况下,建议使用 Task.Run 来执行异步任务。 在C#中,用于同步线程的机制有以下几种: Monitor:用于同步在同一个进程内的线程。 Mutex:用于同步跨进程的线程。 Semaphore:用于控制同时访问某个资源的线程数量。 AutoResetEvent:用于线程之间的通信,允许一个线程在等待另一个线程信号时暂停执行。 ManualResetEvent:与AutoResetEvent类似,但允许多个线程同时等待信号。 CountdownEvent:允许一个线程等待多个线程完成。 Barrier:用于同步多个线程,允许线程在达到某个点时暂停执行。 ReaderWriterLock:允许多个线程同时读取某个资源,但只允许一个线程写入。 SpinLock:用于在高并发情况下保护共享资源。 以上这些同步机制都有其特定的使用场景和优缺点,需要根据具体的需求来选择适合的同步机制。 Invoke与BeginInvoke的不同 不同点: Invoke是同步更新,会阻塞所在工作者线程,BeginInvoke是异步更新,不会阻塞当前线程。 Invoke调用后将指定代码立即插入主线程中执行,而BeginInvoke调用后,发送消息给UI线程, 相当于向Dispatcher队列中添加工作项,待之前UI更新任务完成后,再执行BeginInvoke中的内容。 在使用场景上,Invoke方法会拖延线程直到执行完指定代码,如果需要暂停异步操作,直到用户提供反馈信息,可使用Invoke, 比如指定代码弹出YES/NO对话框,需要根据用户反馈以进一步执行操作的场景。 相同点:两者虽然一个为同步,一个为异步,实际是运行在同一线程,即UI主线程。 常用的委托类型 Action:表示一个不返回值、不带参数的委托。 Func:表示一个带有一个返回值、不带参数的委托。 Predicate:表示一个带有一个返回值、一个参数的委托。 EventHandler:表示一个用于处理事件的委托,它带有两个参数,第一个参数是事件源对象,第二个参数是事件参数。 EventHandler:表示一个用于处理事件的委托,它带有两个参数,第一个参数是事件源对象,第二个参数是事件参数,其中事件参数类型为 TEventArgs。 Action:表示一个不返回值、带有一个参数的委托,参数类型为 T。 Action<T1, T2>:表示一个不返回值、带有两个参数的委托,参数类型分别为 T1 和 T2。 Action<T1, T2, T3>:表示一个不返回值、带有三个参数的委托,参数类型分别为 T1、T2 和 T3。 Func:表示一个带有一个返回值、不带参数的委托,返回值类型为 T。 Func<T, TResult>:表示一个带有一个参数、一个返回值的委托,参数类型为 T,返回值类型为 TResult。 Func<T1, T2, TResult>:表示一个带有两个参数、一个返回值的委托,参数类型分别为 T1 和 T2,返回值类型为 TResult。 Func<T1, T2, T3, TResult>:表示一个带有三个参数、一个返回值的委托,参数类型分别为 T1、T2 和 T3,返回值类型为 TResult。 这些委托类型都是 .NET Framework 中预定义的常用委托类型,你也可以自定义委托类型来满足特定的需求。

2025年7月28日 · 1 分钟 · 105 字

自定义事件

以下是一个使用 Python 实现自定义事件的示例代码: import threading import time class CustomEvent: def __init__(self): self.handlers = set() def add_handler(self, handler): self.handlers.add(handler) def remove_handler(self, handler): self.handlers.remove(handler) def fire(self, *args, **kwargs): for handler in self.handlers: handler(*args, **kwargs) # 创建一个自定义事件 my_event = CustomEvent() # 定义一个事件处理函数 def on_my_event(message): print('Custom event received:', message) # 添加事件处理函数到事件处理程序列表 my_event.add_handler(on_my_event) # 触发自定义事件 my_event.fire('Hello, world!') # 等待一段时间,以便查看输出结果 time.sleep(1) 在这个示例中,我们首先定义了一个名为 CustomEvent 的自定义事件类,其中包含了添加、删除和触发事件处理程序的方法。接着,我们创建了一个 my_event 实例作为自定义事件,并定义了一个名为 on_my_event 的事件处理函数。然后,我们将 on_my_event 函数添加到 my_event 实例的事件处理程序列表中。最后,我们通过调用 my_event.fire 方法触发了自定义事件,并传递了一个消息作为参数。这将导致事件处理程序被调用并输出消息。 需要注意的是,自定义事件的实现方式可能因编程语言而异。在 Python 中,我们可以使用类来实现自定义事件,并使用方法来添加、删除和触发事件处理程序。此外,我们还可以将事件处理程序定义为普通函数,并将它们添加到事件处理程序列表中。最后,我们可以通过调用 fire 方法来触发自定义事件,并传递任意数量的参数。 JavaScript 自定义事件 Demo <!DOCTYPE html> <html> <head> <title>JavaScript 自定义事件 Demo</title> </head> <body> <button id="myButton">点击我</button> <script> // 创建自定义事件 var myEvent = new Event("myEvent"); // 获取按钮元素 var button = document.getElementById("myButton"); // 给按钮添加自定义事件处理程序 button.addEventListener("myEvent", function() { alert("自定义事件被触发了!"); }); // 触发自定义事件 button.dispatchEvent(myEvent); </script> </body> </html>

2025年7月28日 · 1 分钟 · 114 字

vs2019离线安装包制作

vs2009脱机安装 执行layout.cmd,需要网络,如果已经下载localVSlayout,可以跳过这一步 执行cert.cmd,不需要网络. 执行setup.cmd,不需要网络. 脱机安装指引>https://docs.microsoft.com/en-us/visualstudio/install/create-an-offline-installation-of-visual-studio?view=vs-2019 可以参考1,3步命令下载安装其他组件 工作负载和组件id官网地址>https://docs.microsoft.com/en-us/visualstudio/install/workload-component-id-vs-professional?view=vs-2019 命令含义: vs_Professional.exe:前一步下载的 Visual Studio 引导程序 –layout:指定 VS2019 相关组件的下载目录,设置为当前目录下的 vs2019_layout 目录。 –add:添加将要下载的工作负载,添加的是 NativeDesktop、ManagedDesktop –includeRecommended:安装相关工作负载的推荐组件(另一个参数 –includeOptional 将会下载选定的工作负载中可选的组件,会导致下载的安装包过大,不建议使用。) –lang:下载指定的语言包,本人下载了英文(en-US)、中文(zh-CN) %layout.cmd% vs_Professional.exe --layout d:\vs2019\localVSlayout --add Microsoft.VisualStudio.Workload.ManagedDesktop --add Microsoft.VisualStudio.Workload.NetWeb --add Component.GitHub.VisualStudio --includeRecommended --lang en-US pause %cert.cmd% certutil.exe -addstore -f "Root" "D:\vs2019\localVSlayout\certificates\manifestRootCertificate.cer" certutil.exe -addstore -f "Root" "D:\vs2019\localVSlayout\certificates\manifestCounterSignRootCertificate.cer" certutil.exe -addstore -f "Root" "D:\vs2019\localVSlayout\certificates\vs_installer_opc.RootCertificate.cer" pause %setup.cmd% cd localVSlayout vs_Professional.exe --noweb --add Microsoft.VisualStudio.Workload.ManagedDesktop --add Microsoft.VisualStudio.Workload.NetWeb --add Component.GitHub.VisualStudio --includeRecommended pause

2025年7月24日 · 1 分钟 · 67 字

couchdb

CouchDB是一个开源的面向文档的数据库管理系统,建立于 C++ 之上。 ui http://192.168.200.16:5984/_utils/#/_all_dbs curl http://admin:admin@192.168.200.16:5984/_utils/#/_all_dbs curl -X PUT http://admin:admin@192.168.200.16:5984/test curl -X GET http://admin:admin@192.168.200.16:5984/test curl -X DELETE http://admin:admin@192.168.200.16:5984/test curl -X PUT http://admin:admin@192.168.200.16:5984/test/"002" -d "{\"Name\":\"Raju\",\"age\":\"23\"}" curl -X GET http://admin:admin@192.168.200.16:5984/test/"002" {"_id":"002","_rev":"1-d72ed8577c9b1547dec5052cd503b359","Name":"Raju","age":"23"} curl -X PUT http://admin:admin@192.168.200.16:5984/test/"002" -d "{\"Name\":\"Raju\",\"age\":\"24\",\"_rev\":\"1-d72ed8577c9b1547dec5052cd503b359\"}" curl -X GET http://admin:admin@192.168.200.16:5984/test/"002" {"_id":"002","_rev":"2-f2716b3df54d9bbbc7d9b4eafe965354","Name":"Raju","age":"24"} curl -X GET http://admin:admin@192.168.200.16:5984/test/"003" {"_id":"003","_rev":"1-d72ed8577c9b1547dec5052cd503b359","Name":"Raju","age":"23"} curl -X DELETE http://admin:admin@192.168.200.16:5984/test/"003"?rev=1-d72ed8577c9b1547dec5052cd503b359 curl -X GET http://admin:admin@192.168.200.16:5984/test/"003" {"error":"not_found","reason":"deleted"} 查询 ### ip192d168d129d162 POST http://192.168.200.16:5984/ip192d168d129d162/_find authorization: Basic YWRtaW46YWRtaW4= Content-Type: application/json { "selector": { "datetime": { "$gte": 20220517000000000 } }, "sort":[{"datetime":"desc"}], "bookmark":"", "limit": 1000, "fields": ["dateformat","data"], "execution_stats": true }

2025年7月24日 · 1 分钟 · 83 字

github-actions

GitHub Actions是GitHub平台提供的一种持续集成和持续部署(CI/CD)工具。它允许开发者在GitHub上自动化执行各种任务,如构建、测试、部署等。 使用GitHub Actions,您可以创建一个工作流程(Workflow),该工作流程由一个或多个任务(Job)组成,每个任务可以运行在不同的操作系统和环境中。每个任务又由一个或多个步骤(Step)组成,每个步骤可以运行一条或多条命令。 GitHub Actions提供了丰富的预定义的操作(Actions),可以用于执行各种常见的任务,如构建项目、运行测试、发布到云平台等。您还可以编写自己的自定义操作,以满足特定的需求。 通过在代码仓库中创建一个名为.github/workflows的目录,并在该目录中添加一个YAML格式的工作流程文件,您就可以定义和配置您的工作流程。工作流程可以根据事件触发,例如代码的推送、拉取请求的创建等。 GitHub Actions具有强大的集成能力,可以与其他工具和服务进行无缝集成,如Docker、AWS、Azure等。它还提供了丰富的API和工具,用于监控和调试工作流程的执行。 总而言之,GitHub Actions是一种强大的自动化工具,可以帮助开发者简化和加速软件开发过程中的各种任务,并提供了灵活的配置和集成选项。

2025年7月24日 · 1 分钟 · 11 字

jekyll-timezone

Jekyll的时区和时间 2019-12-02 07:00:00 +0800 | 建站 | Jekyll 时区 GitHub Pages 在填写博文Front Matter里的日期时间date变量时如果不带时区字段,一律视为UTC标准时+0000时区,如果要填写其他时区的时间必须指明时区,类似这样2019-12-02 07:00:00 +0800。在_config.yml设置修改timezone变量只关乎日期时间date的显示效果,而不影响真正的时间值。下面详述: 时区的设置 用Jekyll撰写博客时,博文最前面Front Matter头信息中日期时间date变量格式为YYYY-MM-DD HH:MM:SS +/-TTTT,其中+/-TTTT为时区字段,其值为所在时区对UTC标准时的偏移量。如果填写的时间不带时区字段,则一律视为UTC标准时即+0000,而与其他任何设置无关。如果要填写其他时区的时间,必须加上时区字段,这是唯一正确的做法。 如2019-11-13 12:00:00表示UTC标准时中午12点,等价于2019-11-13 12:00:00 +0000。如东八区北京时间早7点要写为2019-12-02 07:00:00 +0800。以下为完整的Front Matter示例: --- layout: post title: Jekyll的时区和时间 date: 2019-12-02 07:00:00 +0800 tags: [Jekyll, 网页编写, 网站建设, GitHub Pages] --- 时区的显示 Jekyll渲染生成静态网页后的显示时间(引用page.date变量),也就是我们要在网页里展示的时间,默认情况下显示的时区是由Jekyll运行环境的操作系统时区决定的。 比如本地操作系统时区为+0800,网页里就显示+0800的时间; 而GitHub Pages系统时区为+0000,显示的就是+0000的时间。 Jekyll会自动将我们之前在date里填写的时间进行时区转换。 上例中我们填写了北京时间2019-12-02 07:00:00 +0800,在本地测试时显示为2019-12-02 07:00:00 +0800 部署到GitHub Pages(使用其内置的Jekyll)时显示为2019-12-01 23:00:00 +0000 改变显示的时区 如果想改变page.date变量显示的时区,可以在_config.yml设置变量timezone,其默认值为操作系统时区,设定值请参考List of tz database time zones。 比如要显示为+0800的北京时间,其值要设为Asia/Shanghai,这样在GitHub Pages就也能显示为如图1的效果了。 timezone: Asia/Shanghai date变量只写了日期或没有设置date变量时的处理 当文章的Front Matter里date变量只写了日期没写时间,Jekyll会将YYYY-MM-DD作为日期;如果未设置data变量,则会将文件名里的YYYY-MM-DD作为日期。然后使用00:00:00作为时间,使用timezone设置的时区作为时区,构成完整的日期时间page.date。 例如一篇博文“2019-12-02-标题.md”中没有设置date变量,则 当timezone为+0800(Asia/Shanghai)时,page.date的时间是2019-12-02 00:00:00 +0800 当timezone为+0000(UTC)时 ,page.date的时间是2019-12-02 00:00:00 +0000 可以注意到这两个日期时间是有时间差的。 Jekyll对时间合理性的检查 这里要提一点,Jekyll生成静态网页时会检查时间的合理性,未来时间的博文不会被渲染生成。比如现在是北京时间2019年12月2日早7点,则将博文Front Matter里的date设置为2019-12-02 09:00:00 +0800是不行的,Jekyll会跳过此文件不处理。 只写日期不写时间时要特别注意,比如现在我的操作系统时间是+0800时区北京时间2019年12月2日早7点,我已经写好了文件名为“2019-12-02-标题.md”的一篇博文,未设置date变量,在本机上运行Jekyll生成浏览正常,但我将此文推送到GitHub Pages上去却没能生成对应的网页。这是因为GitHub Pages上内置的Jekyll所在的操作系统时区为UTC+0000,此时正是2019年12月1日晚23点,这个文件的日期2019年12月2日对于它来说是非法的“未来日期”,此文件被跳过不处理。 此时就要设置_config.yml文件里的timezone变量,将其值设为Asia/Shanghai即+0800东八区,并将此文件一并推送更新到GitHub。如此让GitHub Pages上的Jekyll也按+0800时区处理日期时间,这样2019年12月2日就是合法时间,能正常生成博文了。

2025年7月24日 · 1 分钟 · 91 字

WaitCallback

WaitCallback 委托一般用于将方法异步执行到线程池线程上。线程池是一组可重用的线程,可以在需要时分配线程来执行工作,而不需要创建新的线程。使用线程池可以避免频繁地创建和销毁线程,从而提高应用程序的性能和可伸缩性。 通常,您可以使用 ThreadPool.QueueUserWorkItem 方法将方法异步执行到线程池线程上,并使用 WaitCallback 委托来表示要执行的方法。在这种情况下,WaitCallback 委托的参数是一个 object 类型的对象,该对象可以包含要传递给方法的数据。 例如,以下代码演示了如何使用 WaitCallback 委托和 ThreadPool.QueueUserWorkItem 方法: WaitCallback callback = new WaitCallback(MyMethod); ThreadPool.QueueUserWorkItem(callback, data); private void MyMethod(object state) { // 执行一些工作 } 在这个例子中,MyMethod 方法将在线程池线程上异步执行。data 对象将作为 state 参数传递给 MyMethod 方法。

2025年7月24日 · 1 分钟 · 37 字

yield

在 C# 中,yield 關鍵字用於定義一個迭代器方法(Iterator Method), 這個方法可以產生一個可枚舉的序列。當迭代器方法被調用時, 它不會立即執行方法體中的程式碼,而是返回一個 IEnumerator 對象。 當使用者對這個 IEnumerator 對象調用 MoveNext() 方法時,才會執行迭代器方法中的程式碼,並返回序列中的下一個元素。 使用 yield 關鍵字可以讓我們更簡潔地定義一個可枚舉的序列, 而不需要手動實現 IEnumerator 和 IEnumerable 接口。 此外,在使用 yield 關鍵字時,我們可以使用 yield break 來提前結束序列的產生。 好的,以下是一個使用 yield 關鍵字定義可枚舉序列的簡單範例: using System; using System.Collections.Generic; class Program { static void Main(string[] args) { // 創建一個可枚舉序列 var sequence = CountTo(5); // 使用 foreach 迴圈枚舉序列中的元素 foreach (var number in sequence) { Console.WriteLine(number); } } // 定義一個可枚舉序列,使用 yield 關鍵字 static IEnumerable<int> CountTo(int max) { for (int i = 1; i <= max; i++) { // 使用 yield 關鍵字返回序列中的下一個元素 yield return i; } } } 在這個範例中,我們使用 CountTo 方法定義了一個可枚舉序列,這個序列包含了從 1 到指定的最大值的整數。 在 CountTo 方法中,我們使用 yield return 關鍵字返回序列中的下一個元素,這個方法被調用時不會立即執行, 而是返回一個 IEnumerable<int> 對象。在 Main 方法中,我們使用 foreach 迴圈來枚舉這個序列中的元素,這時才會執行 CountTo 方法中的程式碼並產生序列。 ...

2025年7月24日 · 1 分钟 · 175 字