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