主頁 > .NET開發 > 對簡單的Quartz了解的不簡單一些

對簡單的Quartz了解的不簡單一些

2020-09-13 11:36:43 .NET開發

Quartz.Net的關鍵介面

  • Scheduler - 與調度程式互動的主要API,[ IScheduler]
  • Job - 由希望由調度程式執行的組件實作的介面,[IJob]
  • JobDetail - 用于定義作業的實體,[IJobDetail]
  • Trigger(即觸發器) - 定義執行給定作業的計劃的組件,[ITrigger]
  • JobBuilder - 用于定義/構建JobDetail實體,用于定義作業的實體,
  • TriggerBuilder - 用于定義/構建觸發器實體,
    public class HelloJob : IJob
    {
        public Task Execute(IJobExecutionContext context)
        {
            Console.WriteLine("Hello World");
            return Task.CompletedTask;
        }
    }
    
    class Program
    {
        static async Task Main(string[] args)
        {
            StdSchedulerFactory factory = new StdSchedulerFactory();
            var scheduler = await factory.GetScheduler();
            await scheduler.Start();
            // define the job and tie it to our HelloJob class
            IJobDetail job = JobBuilder.Create<HelloJob>()
                .WithIdentity("myJob", "group1")
                .Build();

            // Trigger the job to run now, and then every 3 seconds
            ITrigger trigger = TriggerBuilder.Create()
                .WithIdentity("myTrigger", "group1")
                .StartNow()
                .WithSimpleSchedule(x => x
                    .WithIntervalInSeconds(3)
                    .RepeatForever())
            .Build();
            await scheduler.ScheduleJob(job, trigger);

            // 30秒后停止調度計劃
            await Task.Delay(1000 * 30);
            await scheduler.Shutdown();
        }
    }

Scheduler

Scheduler的生命期,從SchedulerFactory創建它時開始,到Scheduler呼叫Shutdown()方法時結束,

Scheduler被創建后,可以增加、洗掉和列舉Job和Trigger,以及執行其它與調度相關的操作(如暫停Trigger),

Scheduler只有在呼叫Start()方法后,才會真正地觸發trigger ,

Job與JobDetail

下面講到的 Job 都是指的是實作 IJob 的類,例如:HelloJob

  • JobDetail

    1. JobDetail實體是通過JobBuilder類創建的,
        // define the job and tie it to our HelloJob class
        IJobDetail job = JobBuilder.Create<HelloJob>()
            .WithIdentity("myJob", "group1")
            .Build();
    
    1. 注冊到 Scheduler 的不是Job物件,而是 JobDetail 實體 ,Job物件只是 JobDetail 實體的一部分,
        await scheduler.ScheduleJob(job, trigger);
    

    可以只創建一個job類,然后創建多個與該Job關聯的JobDetail實體,每一個實體都有自己的屬性集和JobDataMap,最后,將所有的實體都加到scheduler中 ,

    1. 通過JobDetail物件,可以給job實體配置的其它屬性有:

      Durability:[StoreDurably()]如果一個job是非持久的,當沒有活躍的trigger與之關聯的時候,會被自動地從scheduler中洗掉,也就是說,非持久的job的生命期是由trigger的存在與否決定的;

      RequestsRecovery:[RequestRecovery()]如果一個job是可恢復的,并且在其執行的時候,scheduler發生硬關閉(hard shutdown)(比如運行的行程崩潰了,或者關機了),則當scheduler重新啟動的時候,該job會被重新執行,此時,該job的JobExecutionContext.isRecovering() 回傳true,

  • Job

    1. 每一個Job都必須實作IJob,例如上面的 HelloJob,這個類僅僅表明該job需要完成什么型別的任務,除此之外,Quartz還需要知道該Job實體所包含的屬性,這將由JobDetail類來完成,

          public class HelloJob : IJob
          {
              public Task Execute(IJobExecutionContext context)
              {
                  Console.WriteLine("Hello World");
                  return Task.CompletedTask;
              }
          }
      
    2. Job的生命周期

        // define the job and tie it to our HelloJob class
        IJobDetail job = JobBuilder.Create<HelloJob>()
            .WithIdentity("myJob", "group1")
            .Build();
    
        // Trigger the job to run now, and then every 40 seconds
        ITrigger trigger = TriggerBuilder.Create()
            .WithIdentity("myTrigger", "group1")
            .StartNow()
            .WithSimpleSchedule(x => x
                .WithIntervalInSeconds(3)
                .RepeatForever())
        	.Build();
        await scheduler.ScheduleJob(job, trigger);
    

    可以看到,我們傳給scheduler一個JobDetail實體,因為我們在創建JobDetail時,將要執行的job的類名(HelloJob)傳給了JobDetail,所以scheduler就知道了要執行何種型別的job;

    每次當scheduler執行job時,在呼叫其Execute(…)方法之前會創建該類的一個新的實體;執行完畢,對該實體的參考就被丟棄了,實體會被垃圾回收,

    Job 的實體要到該執行它們的時候才會實體化出來,每次 Job 被執行,一個新的 Job 實體會被創建,也就是說 Job 不必擔心執行緒安全性,因為同一時刻僅有一個執行緒去執行給定 Job 類的實體,甚至是并發執行同一 Job 也是如此,

    這種執行策略需要我們注意:
    
    * job必須有一個無參的建構式;
    * 在job類中,不應該定義有狀態的資料屬性,因為在job的多次執行中,這些屬性的值不會保留, 
    

    那么如何給job實體增加屬性或配置呢?如何在job的多次執行中,跟蹤job的狀態呢?答案就是:JobDataMap,JobDetail物件的一部分, (后面會詳細介紹用法)

Trigger

Trigger物件是用來觸發執行Job的,當調度一個job時,我們實體一個觸發器然后調整它的屬性來滿足job執行的條件,觸發器也有一個和它相關的JobDataMap,它是用來給被觸發器觸發的job傳引數的,

  1. SimpleTrigger可以滿足的調度需求是:在具體的時間點執行一次,或者在具體的時間點執行,并且以指定的間隔重復執行若干次,

    	ITrigger trigger = TriggerBuilder.Create()
    		.WithIdentity("myTrigger", "group1")
    		.StartNow()
    		.WithPriority(5)
    		.WithSimpleSchedule(x => x
    			.WithIntervalInSeconds(3)
    			.RepeatForever())
    		.Build();
    
  2. CronTrigger基于日歷的概念進行作業啟動計劃,

    	ITrigger trigger1 = TriggerBuilder.Create()
    		.WithIdentity("myTrigger1", "group1")
    		.ForJob(job)
    		.WithCronSchedule("0 0/3 * * ?")
            .Build();
    

    CronExpression運算式 :https://www.cnblogs.com/yaowen/p/3779284.html

  3. 優先級(priority) 方法:.WithPriority(5)

如果你的trigger很多(或者Quartz執行緒池的作業執行緒太少),Quartz可能沒有足夠的資源同時觸發所有的trigger;這種情況下,你可能希望控制哪些trigger優先使用Quartz的作業執行緒,要達到該目的,可以在trigger上設定priority屬性,

比如,你有N個trigger需要同時觸發,但只有Z個作業執行緒,優先級最高的Z個trigger會被首先觸發,如果沒有為trigger設定優先級,trigger使用默認優先級,值為5;priority屬性的值可以是任意整數,正數、負數都可以,(只有同時觸發的trigger之間才會比較優先級,10:59觸發的trigger總是在11:00觸發的trigger之前執行,)

Job與Trigger

Trigger對于job而言就好比一個驅動器;沒有觸發器來定時驅動作業,作業就無法運行;

對于Job而言,一個job可以對應多個Trigger,但對于Trigger而言,一個Trigger只能對應一個job;所以一個Trigger 只能被指派給一個 Job;如果你需要一個更復雜的觸發計劃,你可以創建多個 Trigger 并指派它們給同一個 Job,(Trigger實體對應一個JobDetail實體,Job類可以添加到多個JobDetail實體中)

Scheduler 是基于配置在 Job上的 Trigger 來決定正確的執行計劃的,

JobDataMap

重點介紹一下JobDataMap,這是一個非常好用且被我們忽視的屬性,

JobDataMap中可以包含不限量的(序列化的)資料物件,在job實體執行的時候,可以使用其中的資料;JobDataMap是 IDictionary<TKey, TValue> 介面的一個實作,額外增加了一些便于存取基本型別的資料的方法,

JobDetail 和 Trigger 示例都可以設定 JobDataMap(通過UsingJobData()方法)

	IJobDetail job = JobBuilder.Create<HelloJob>()
		.WithIdentity("myJob", "group1")
		.UsingJobData("jobDetail", "J")
		.Build();
	
	ITrigger trigger = TriggerBuilder.Create()
		.WithIdentity("myTrigger", "group")
		.UsingJobData("trigger", "T")
		.WithCronSchedule("0/3 * * * * ?")
		.Build();
	await scheduler.ScheduleJob(job, trigger);

傳遞的值可以通過IJob.Execute(IJobExecutionContext context) 中context.MergedJobDataMap獲取

    public class HelloJob : IJob
    {
        public Task Execute(IJobExecutionContext context)
        {
            var jobDataMap = context.MergedJobDataMap;
            Console.WriteLine($"{DateTime.Now.ToLongTimeString()}-jobDetail:{jobDataMap["jobDetail"]}-trigger:{jobDataMap["trigger"]}");
            return Task.CompletedTask;
        }
    }

執行結果:

11:36:36-jobDetail:J-trigger:T
11:36:39-jobDetail:J-trigger:T
11:36:42-jobDetail:J-trigger:T
...

需要注意的是:

  1. MergedJobDataMap是將JobDetail.JobDataMap和Trigger.JobDataMap的值合并的,如果key重復,將讀取Trigger中相同可以的值,
  2. 可以通過 context.JobDetail.JobDataMap 和 context.Trigger.JobDataMap分別讀取,
  3. 在IJob.Execute()方法中修改任何JobDataMap值,是不會影響到下次Job執行JobDataMap的值的,只在本次Job中有效,

那有什么辦法讓本次執行的狀態修改,影響到以一次執行呢?即修改JobDataMap的值,下一次執行取出的是上一次修改過的?辦法是有的,給Job類打標簽

Job屬性標簽

PersistJobDataAfterExecution:將該注解加在job類上,告訴Quartz在成功執行了job類的execute方法后(沒有發生任何例外),更新JobDetail中JobDataMap的資料,使得該job(即JobDetail)在下一次執行的時候,JobDataMap中是更新后的資料,而不是更新前的舊資料,

    [PersistJobDataAfterExecution]
    public class HelloJob : IJob
    {
        public Task Execute(IJobExecutionContext context)
        {
            var count = context.JobDetail.JobDataMap.GetInt("Count");
            Console.WriteLine($"{DateTime.Now.ToLongTimeString()}-Count:{count}");

            count++;
            context.JobDetail.JobDataMap.Put("Count", count);

            return Task.CompletedTask;
        }
    }                                       
	StdSchedulerFactory factory = new StdSchedulerFactory();
	var scheduler = await factory.GetScheduler();
	await scheduler.Start();
	
	IJobDetail job1 = JobBuilder.Create<HelloJob>()
		.WithIdentity("myJob.1", "group")
		.UsingJobData("Count", "1")
		.Build();
	
	ITrigger trigger1 = TriggerBuilder.Create()
		.WithIdentity("myTrigger.1", "group")
		.WithCronSchedule("0/3 * * * * ?")
		.Build();
					
	await scheduler.ScheduleJob(job1, trigger1);  

執行結果:

14:17:09-Count:1
14:17:12-Count:2
14:17:15-Count:3
...

如果使用了[PersistJobDataAfterExecution]標簽,將強烈建議同時使用[DisallowConcurrentExecution]標簽,因為當同一個job(JobDetail)的兩個實體被并發執行時,由于競爭,JobDataMap中存盤的資料很可能是不確定的,

DisallowConcurrentExecution:將該注解加到job類上, 告訴Quartz不要并發地執行同一個job定義的多個實體 ,

舉例說明就是:將 HelloJob 添加到 job1(IJobDetail )

	IJobDetail job1 = JobBuilder.Create<HelloJob>()
		.WithIdentity("myJob.1", "group")
		.UsingJobData("flag", "myJob.1")
		.Build();

job1系結觸發器trigger1(ITrigger) 每秒執行一次

	ITrigger trigger1 = TriggerBuilder.Create()
		.WithIdentity("myTrigger.1", "group")
		.WithCronSchedule("0/1 * * * * ?")
		.Build();
	await scheduler.ScheduleJob(job1, trigger1);

HelloJob 的執行耗時為2秒

    public class HelloJob : IJob
    {
        public Task Execute(IJobExecutionContext context)
        {
            var flag = context.MergedJobDataMap.GetString("flag");
            //模擬耗時2秒
            Thread.Sleep(2000);
            Console.WriteLine($"{DateTime.Now.ToLongTimeString()}-flag:{flag}");

            return Task.CompletedTask;
        }
    }

首先不打 DisallowConcurrentExecution 標簽,看看輸出結果:

任務啟動:14:39:05
14:39:07-flag:myJob.1
14:39:08-flag:myJob.1
14:39:09-flag:myJob.1
14:39:10-flag:myJob.1
...

通過結果輸入,可以看到,第一次任務是從14:39:05開始,14:39:07結束;第二次的任務接時間是14:39:08,退出開始時間是14:39:06,以此類推...也就說前一個任務未完成,并不影響之后任務的開始

接著我們個 HelloJob 打上 DisallowConcurrentExecution 屬性標簽

    [DisallowConcurrentExecution]
    public class HelloJob : IJob
    {
        public Task Execute(IJobExecutionContext context)
        {
            var flag = context.MergedJobDataMap.GetString("flag");
            //模擬耗時2秒
            Thread.Sleep(2000);
            Console.WriteLine($"{DateTime.Now.ToLongTimeString()}-flag:{flag}");

            return Task.CompletedTask;
        }
    }

看看輸出結果:

任務啟動:14:47:34
14:47:36-flag:myJob.1
14:47:38-flag:myJob.1
14:47:40-flag:myJob.1
14:47:42-flag:myJob.1
...

通過結果輸出可以看到,文本輸出是每兩秒一次,也就說,前一個任務未完成,之后任務不會開始,即不會創建一個新的 HelloJob 實體,這也就不會并發處理任務了,

需要注意的是,DisallowConcurrentExecution 屬性標簽,限制的是 JobDetail ,而不是 Job(HelloJob),同一個JobDetail 實體創建的 Job 不會并發,但,不同的 JobDetail 實體創建的 Job 是可以并發的,

我們再創建一組關于 HelloJob 的任務:job2(IJobDetail),trigger2(ITrigger),HelloJob 不變,

    class Program
    {
        static async Task Main(string[] args)
        {
            Console.WriteLine($"任務啟動:{DateTime.Now.ToLongTimeString()}");
            StdSchedulerFactory factory = new StdSchedulerFactory();
            var scheduler = await factory.GetScheduler();
            await scheduler.Start();
            IJobDetail job1 = JobBuilder.Create<HelloJob>()
                .WithIdentity("myJob.1", "group")
                .UsingJobData("flag", "myJob.1")
                .Build();
            ITrigger trigger1 = TriggerBuilder.Create()
                .WithIdentity("myTrigger.1", "group")
                .WithCronSchedule("0/1 * * * * ?")
                .Build();
            
            IJobDetail job2 = JobBuilder.Create<HelloJob>()
                .WithIdentity("myJob.2", "group")
                .UsingJobData("flag", "myJob.2")
                .Build();
            ITrigger trigger2 = TriggerBuilder.Create()
                .WithIdentity("myTrigger.2", "group")
                .WithCronSchedule("0/1 * * * * ?")
                .Build();

            await scheduler.ScheduleJob(job1, trigger1);
            await scheduler.ScheduleJob(job2, trigger2);

            Console.ReadKey();
        }
    }

看看輸出結果:

任務啟動:15:02:24
15:02:26-flag:myJob.1
15:02:26-flag:myJob.2
15:02:28-flag:myJob.1
15:02:28-flag:myJob.2
15:02:30-flag:myJob.1
15:02:30-flag:myJob.2
...

通過結果輸出可以看到,同一個JobDetail,是沒有每秒執行的,即前一個任務沒有完成,后面的任務不會執行,但不同的JobDetail,卻在同一時間執行了,

Job的配置

像上面示例中,我們配置Job,基本都是硬編碼,我們可以把配置移到組態檔中,方便修改和添加

默認組態檔名:quartz_jobs.xml

<?xml version="1.0" encoding="UTF-8"?>

<job-scheduling-data xmlns="http://quartznet.sourceforge.net/JobSchedulingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0">
  <processing-directives>
    <overwrite-existing-data>true</overwrite-existing-data>
  </processing-directives>

  <schedule>
    <job>
      <name>myJob.1</name>
      <group>group</group>
      <description>Hello World!</description>
      <job-type>Sayook.Schedule.Client.HelloJob, Sayook.Schedule.Client</job-type>
      <job-data-map>
        <entry>
          <key>flag</key>
          <value>myJob.1</value>
        </entry>
      </job-data-map>
    </job>
    <trigger>
      <cron>
        <name>myTrigger.1</name>
        <group>group</group>
        <description>Hello World! </description>
        <job-name>myJob.1</job-name>
        <job-group>group</job-group>
        <job-data-map>
          <entry>
            <key>key</key>
            <value>1</value>
          </entry>
        </job-data-map>
        <cron-expression>0/3 * * * * ?</cron-expression>
      </cron>
    </trigger>
  </schedule>
</job-scheduling-data>

我們可以將 job_scheduling_data_2_0.xsd 檔案添加到 VisualStudio2019 的XML架構(直接在IDE頂部搜索框搜索xml架構),我們在撰寫xml組態檔的時候就會有提示和驗證了,

    class Program
    {
        static async Task Main(string[] args)
        {
            var properties = new NameValueCollection
            {
                ["quartz.plugin.xml.type"] = "Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz.Plugins",
                ["quartz.plugin.xml.fileNames"] = "quartz_jobs.xml",
                // this is the default
                ["quartz.plugin.xml.FailOnFileNotFound"] = "true",
                // this is not the default
                ["quartz.plugin.xml.failOnSchedulingError"] = "true"
            };

            StdSchedulerFactory factory = new StdSchedulerFactory(properties);
            var scheduler = await factory.GetScheduler();
            await scheduler.Start();
            Console.ReadKey();
        }
    }

["quartz.plugin.xml.FailOnFileNotFound"] = "true",
["quartz.plugin.xml.failOnSchedulingError"] = "true"

上面兩個組態檔強烈建議添加,以為這樣,組態檔錯誤了,會有詳細的例外資訊拋出,以便修改,負責是不會報錯,很難定位問題,

使用組態檔,要參考包:Quartz.Plugins

相關配置可查看文章:Quartz.NET 組態檔詳解 https://www.cnblogs.com/abeam/p/8044460.html

應用示例

基于.NetCore的依賴注入,對Quartz.Net的使用

示例里面包含:

  1. 可視化面板控制的調度應用[Sayook.Schedule.Manager]
  2. 使用控制臺應用程式創建的 泛型主機 呼叫應用[Sayook.Schedule.Client]

通過xml檔案配置Job,后續維護、新增Job,對代碼的改動都非常小, 是最輕量級的使用,

示例代碼地址: https://gitee.com/sayook/Sayook.Schedule.Framework

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/24079.html

標籤:.NET Core

上一篇:.net core部署至Centos上填坑技巧

下一篇:一、命令列創建專案

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more