延时:
async Task DelayMethod()
{
("Before Delay");
await (1000); // 延时1秒
("After Delay");
}
类构造方法:
在C#中,可以使用Task类来创建和管理多线程任务。以下是Task的启动、暂停、继续和结束的方法:
启动Task:
1. 使用()方法启动一个新的Task。
2. 使用()方法启动一个新的Task。
3. 使用Task构造函数创建一个新的Task,然后调用()方法启动它。
暂停Task:
1. 使用ManualResetEvent类创建一个事件对象,然后在Task中使用WaitOne()方法暂停Task。
2. 使用CancellationTokenSource类创建一个取消令牌对象,然后在Task中使用该对象的Cancel()方法暂停Task。
继续Task:
1. 使用ManualResetEvent类的Set()方法继续Task。
2. 使用CancellationTokenSource类的Cancel()方法取消暂停Task。
结束Task:
1. 使用()方法等待Task完成。
2. 使用()方法等待多个Task完成。
3. 使用()方法等待任何一个Task完成。
4. 使用Task的CancellationTokenSource对象的Cancel()方法取消Task。
public Task(Action action);
public Task(Action action, CancellationToken 取消令牌);
public Task(Action action, TaskCreationOptions 创建选项);
public Task(Action<object> action, object 状态);
public Task(Action action, CancellationToken 取消令牌, TaskCreationOptions 创建选项);
public Task(Action<object> action, object 状态, CancellationToken 取消令牌);
public Task(Action<object> action, object 状态, TaskCreationOptions 创建选项);
public Task(Action<object> action, object 状态, CancellationToken 取消令牌, TaskCreationOptions 创建选项);
public static Task Run(Func<Task> function, CancellationToken cancellationToken关闭线程)
// ctrl+F查找,3312行
第二参数:用于关闭线程。(字段位置)
//CancellationToken ct = new CancellationToken();
private CancellationTokenSource cts ;//关闭线程用(包含ct)
private ManualResetEvent resetEvent = new ManualResetEvent(true);// 暂停业务
public static async Task Main()
{
var tokenSource = new CancellationTokenSource();// 令牌对象
var token = ;// 开关绑定
var files = new List<Tuple<string, string, long, DateTime>>();
var t = ( () => { string dir = "C:\\Windows\\System32\\";
object obj = new Object();
if ((dir)) {
((dir),
f => {
if ()
();
var fi = new FileInfo(f);
lock(obj) {
((, , , ));
}
});
}
}
, token);// 开关绑定
await ();
();// 令牌对象
【1】新建线程:
cts = new CancellationTokenSource();//关闭线程用
Task task1 = new Task(() =>
{
//在这个地方编写我们需要的逻辑...
}, );
();// 这句才开启
(() => {
canopen.set_reg32bit(Convert.ToInt16(, 16), 0, Convert.ToInt32(, 16));
});
(() =>
{
//这里填业务。。。。。
i= canopen.get_region(Convert.ToInt16(, 16), (, 16));
(new Action(() =>
{
//这里填窗体业务。。。。。
= ("X8");
}));
}
);
(() =>
{
canopen.set_reg32bit(0x1017, 0x00, 7000);
} );
Task<int> task = (() =>
{
// 这里是需要执行的代码
return 42;
});
// 这里可以执行一些其他的代码
int result = ; // 这里会阻塞当前线程,直到任务完成并返回结果
(result); // 输出 42
//启动线程
private void btnStart_Click(object sender, EventArgs e)
{
cts = new CancellationTokenSource();// 令牌
CancellationToken ct = ; //获取令牌
Task task = new Task(async () =>
{
while (true)
{
if (!)
{
();
//在下面编写你要让线程完成的任务...
("任务执行中...");
await (1000);
}
else
{
return;// 结束线程
}
}
}, ct);
();
//();// 结束线程
}
//暂停 ();
//继续 ();
//结束 ();
令牌结束,没有作用,实际还是要方法体内判断状态,如果是结束,退出方法体,线程才真正结束。 ();只是将只读属性True,方法体内return才真正结束线程。
Task task3 = (() =>
{
//在这个地方编写我们需要的逻辑...
});
【2】排队等待:
Task task1 = new Task(() =>
{
(1000);
($"Task1子线程Id={} {()}");
});
();
Task task2 = new Task(() =>
{
(2000);
($"Task2子线程Id={} {()}");
});
();
//第1种方式:挨个等待和前面一样
//();
//();
//第2种方式:等待所有的任务完成 【推荐】
//(task1, task2);
//第3种方式:等待任何一个完成即可 【推荐】
(task1, task2);
("主线程开始运行!Time=" + ());
//线程的延续(主线程不等待,子线程依次执行,如果你需要主线程也按照子线程的顺序来,请你自己把主线程的任务放到延续任务中就可以)
(task1, task2).ContinueWith(task3 =>
{
//在这里可以编写你需要的业务...
($"Task3子线程Id={} {()}");
});
("主线程开始运行!Time=" + ());
12都完成3才run
//线程的延续(主线程不等待,子线程任何一个执行完毕,就会执行后面的线程)
(task1, task2).ContinueWith(task3 =>
{
//在这里可以编写你需要的业务...
($"Task3子线程Id={} {()}");
});
有1个完成,3就run
Task parentTask = new Task(() =>
{
Task task1 = new Task(() =>
{
(1000);
}, );
Task task2 = new Task(() =>
{
(3000);
}, );
();
();
});
();
();//等待附加的子任务全部完成。相当于(taks1,task2);
//如果这个枚举参数不添加,主线程会直接运行,不等待
("主线程开始执行!Time= " + ());
耗时线程:
//长时间的任务运行,需要采取的方法
Task task1 = new Task(() =>
{
(2000);
}, );
//LongRunning:如果你明确知道这个任务是长时间运行的,建议你加上。当然你使用Thread也是可以的。但是不要使用
//ThreadPool,因为长时间占用不归还线程,系统会强制开启新的线程,会一定程度影响性能
();
();
("主线程开始执行!Time= " + ());
【3】线程取消:
using System;
using ;
using ;
class Program
{
static async Task Main()
{
var tokenSource2 = new CancellationTokenSource();
CancellationToken ct = ;
var task = (() =>
{
// Were we already canceled?
();
bool moreToDo = true;
while (moreToDo)
{
// Poll on this property if you have to do
// other cleanup before throwing.
if ()
{
// Clean up here, then...
();
}
}
}, ); // Pass same token to .
();
// Just continue on this thread, or await with try-catch:
try
{
await task;
}
catch (OperationCanceledException e)
{
($"{nameof(OperationCanceledException)} thrown with message: {}");
}
finally
{
();
}
();
}
}
// /zh-cn/dotnet/standard/parallel-programming/task-cancellation
任务取消 | Microsoft Learn
//创建取消信号源对象
CancellationTokenSource cts = new CancellationTokenSource();
Task task = (() =>
{
while (!)//判断任务是否被取消
{
(200);
($"子线程Id={} {()}");
}
}, );
//我们在这个地方模拟一个事件产生
(2000);
();//取消任务,只要传递这样一个信号就可以
CancellationTokenSource cts = new CancellationTokenSource();
Task task = (() =>
{
while (!)
{
(500);
($"子线程Id={} {()}");
}
}, );
//注册一个委托:这个委托将在任务取消的时候调用
(() =>
{
//在这个地方可以编写自己要处理的逻辑...
("任务取消,开始清理工作......");
(2000);
("任务取消,清理工作结束......");
});
//这个地方肯定是有其他的逻辑来控制取消
(3000);//模拟其他的耗时工作
();//取消任务
限时任务:
(3000); //3秒后自动取消
重新开的线程,需要用新的cts绑定。
Lock锁:锁对象,必须是引用类型
//为什么要用锁?在多线程中,尤其是静态资源的访问,必然会有竞争
private static int nums = 0;
private static object myLock = new object();// 必须引用类型
static void Method12()
{
for (int i = 0; i < 5; i++)
{
(() =>
{
TestMethod();
});
}
}
static void TestMethod()
{
for (int i = 0; i < 100; i++)
{
lock (myLock)// 锁住引用类型
{
nums++;
(nums);
}
}
}
//Lock是Monitor语法糖,本质是解决资源的锁定问题
//我们锁住的资源一定是让线程可访问到的,所以不能是局部变量。
//锁住的资源千万不要是值类型。
//lock也不能锁住string类型。
lock内,对象是同一个,才能进入
using System;
using ;
using ;
using ;
public class Example
{
public static void Main()
{
// Define the cancellation token.
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken token = ;
Random rnd = new Random();
Object lockObj = new Object();
List<Task<int[]>> tasks = new List<Task<int[]>>();
TaskFactory factory = new TaskFactory(token);
for (int taskCtr = 0; taskCtr <= 10; taskCtr++) {
int iteration = taskCtr + 1;
(( () => {
int value;
int[] values = new int[10];
for (int ctr = 1; ctr <= 10; ctr++) {
lock (lockObj) {
value = (0,101);
}
if (value == 0) {
();
("Cancelling at task {0}", iteration);
break;
}
values[ctr-1] = value;
}
return values;
}, token));
}
try {
Task<double> fTask = ((),
(results) => {
("Calculating overall mean...");
long sum = 0;
int n = 0;
foreach (var t in results) {
foreach (var r in ) {
sum += r;
n++;
}
}
return sum/(double) n;
} , token);
("The mean is {0}.", );
}
catch (AggregateException ae) {
foreach (Exception e in ) {
if (e is TaskCanceledException)
("Unable to compute mean: {0}",
((TaskCanceledException) e).Message);
else
("Exception: " + ().Name);
}
}
finally {
();
}
}
}
// Repeated execution of the example produces output like the following:
// Cancelling at task 5
// Unable to compute mean: A task was canceled.
//
// Cancelling at task 10
// Unable to compute mean: A task was canceled.
//
// Calculating overall mean...
// The mean is 5.29545454545455.
//
// Cancelling at task 4
// Unable to compute mean: A task was canceled.
//
// Cancelling at task 5
// Unable to compute mean: A task was canceled.
//
// Cancelling at task 6
// Unable to compute mean: A task was canceled.
//
// Calculating overall mean...
// The mean is 4.97363636363636.
//
// Cancelling at task 4
// Unable to compute mean: A task was canceled.
//
// Cancelling at task 5
// Unable to compute mean: A task was canceled.
//
// Cancelling at task 4
// Unable to compute mean: A task was canceled.
//
// Calculating overall mean...
// The mean is 4.86545454545455.