一、Thread
1、Thread简单实例(创建、运行)
class Program
{
static void Main(string[] args)
{
Thread thread = new Thread(() =>
{
Console.WriteLine("测试线程的资源占用情况!");
});
thread.IsBackground = true;
thread.Start();
}
}
2、Thread线程(开启、暂停、继续、中断、终止)
private Thread thread = null;
private int counter = 0;
//【1】开启
private void btnStart_Click(object sender, EventArgs e)
{
thread = new Thread(() =>
{
while (true)
{
try
{
Thread.Sleep(500);
lblInfo.Invoke(new Action(() =>
{
lblInfo.Text += counter++ + ",";
}));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + " 异常位置:" + counter++);
}
}
});
thread.Start();
}
//暂停
private void btnSuspend_Click(object sender, EventArgs e)
{
if (thread.ThreadState == ThreadState.Running ||
thread.ThreadState == ThreadState.WaitSleepJoin)
{
thread.Suspend();
}
}
//继续
private void btnResume_Click(object sender, EventArgs e)
{
if (thread.ThreadState == ThreadState.Suspended )
{
thread.Resume();
}
}
//中断
private void btnInterrupt_Click(object sender, EventArgs e)
{
thread.Interrupt();
}
//终止
private void btnAbort_Click(object sender, EventArgs e)
{
thread.Abort();
}
3、Thread阻塞
class Program
{
static void Main(string[] args)
{
Thread thread = new Thread(new ThreadStart(() =>
{
Thread.Sleep(3000);
Console.WriteLine("这个是正在执行的子线程数据......");
}));
thread.Start();
thread.Join();//会等待子线程执行完毕后,在执行下面的主线程内容。
Console.WriteLine("这个是主线程的数据...");
}
}
4、Thread基本属性
二、ThreadPool线程池
1、线程池ThreadPool的基本使用
static void Method1()
{
ThreadPool.QueueUserWorkItem((arg) =>
{
//请在这里编写实际的要处理的内容...
Console.WriteLine("子线程Id:" + Thread.CurrentThread.ManagedThreadId);
});
Console.WriteLine("主线程Id:" + Thread.CurrentThread.ManagedThreadId);
}
static void Method2()
{
ThreadPool.QueueUserWorkItem((arg) =>
{
//请在这里编写实际的要处理的内容...
Console.WriteLine("子线程Id:" + Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("arg=" + arg);
}, "xiketang.ke.qq.com");
Console.WriteLine("主线程Id:" + Thread.CurrentThread.ManagedThreadId);
}
三、Task
1、 Task的使用多线程任务的开启3种方式
//【1】通过new的方式创建一个Task对象,并启动
static void Method1_1()
{
Task task1 = new Task(() =>
{
//在这个地方编写我们需要的逻辑...
Console.WriteLine($"new一个新的Task启动的子线程Id={Thread.CurrentThread.ManagedThreadId}");
});
task1.Start();
}
//【2】使用Task的Run()方法
static void Method1_2()
{
Task task2 = Task.Run(() =>
{
//在这个地方编写我们需要的逻辑...
Console.WriteLine($"使用Task的Run()方法开启的子线程Id={Thread.CurrentThread.ManagedThreadId}");
});
}
//【3】使用TaskFactory启动(类似于ThreadPool)
static void Method1_3()
{
Task task3 = Task.Factory.StartNew(() =>
{
//在这个地方编写我们需要的逻辑...
Console.WriteLine($"使用TaskFactory开启的子线程Id={Thread.CurrentThread.ManagedThreadId}");
});
}
2、Task使用Task的阻塞方式和任务延续
//Task各种【阻塞】方式(3个)
static void Method1()
{
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 希望前面所有任务执行完毕后,再继续执行后面的线程,和前面相比,既有阻塞,又有延续。
static void Method2()
{
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();
//线程的延续(主线程不等待,子线程依次执行,如果你需要主线程也按照子线程的顺序来,请你自己把主线程的任务放到延续任务中就可以)
Task.WhenAll(task1, task2).ContinueWith(task3 =>
{
//在这里可以编写你需要的业务...
Console.WriteLine($"Task3子线程Id={Thread.CurrentThread.ManagedThreadId} {DateTime.Now.ToLongTimeString()}");
});
Console.WriteLine("主线程开始运行!Time=" + DateTime.Now.ToLongTimeString());
}
//Task的延续:WhenAny
static void Method3()
{
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();
//线程的延续(主线程不等待,子线程任何一个执行完毕,就会执行后面的线程)
Task.WhenAny(task1, task2).ContinueWith(task3 =>
{
//在这里可以编写你需要的业务...
Console.WriteLine($"Task3子线程Id={Thread.CurrentThread.ManagedThreadId} {DateTime.Now.ToLongTimeString()}");
});
Console.WriteLine("主线程开始运行!Time=" + DateTime.Now.ToLongTimeString());
}
3、Task使用Task常见枚举 TaskCreationOptions(父子任务运行、长时间运行的任务处理)
//请大家通过Task的构造方法,观察TaskCreationOptions这个枚举的类型,自己通过F12查看
static void Method1()
{
Task parentTask = new Task(() =>
{
Task task1 = new Task(() =>
{
Thread.Sleep(1000);
Console.WriteLine($"Task1子线程Id={Thread.CurrentThread.ManagedThreadId} {DateTime.Now.ToLongTimeString()}");
}, TaskCreationOptions.AttachedToParent);
Task task2 = new Task(() =>
{
Thread.Sleep(3000);
Console.WriteLine($"Task2子线程Id={Thread.CurrentThread.ManagedThreadId} {DateTime.Now.ToLongTimeString()}");
}, TaskCreationOptions.AttachedToParent);
task1.Start();
task2.Start();
});
parentTask.Start();
parentTask.Wait();//等待附加的子任务全部完成。相当于Task.WaitAll(taks1,task2);
//TaskCreationOptions.AttachedToParent如果这个枚举参数不添加,主线程会直接运行,不等待
Console.WriteLine("主线程开始执行!Time= " + DateTime.Now.ToLongTimeString());
}
//长时间的任务运行,需要采取的方法
static void Method2()
{
Task task1 = new Task(() =>
{
Thread.Sleep(2000);
Console.WriteLine($"Task1子线程Id={Thread.CurrentThread.ManagedThreadId} {DateTime.Now.ToLongTimeString()}");
}, TaskCreationOptions.LongRunning);
//LongRunning:如果你明确知道这个任务是长时间运行的,建议你加上。当然你使用Thread也是可以的。但是不要使用
//ThreadPool,因为长时间占用不归还线程,系统会强制开启新的线程,会一定程度影响性能
task1.Start();
task1.Wait();
Console.WriteLine("主线程开始执行!Time= " + DateTime.Now.ToLongTimeString());
}
4、Task使用Task中的取消功能:使用的是CacellationTokenSoure解决多任务中协作取消和超时取消方法
//【1】Task任务的取消和判断
static void Method1()
{
//创建取消信号源对象
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();//取消任务,只要传递这样一个信号就可以
}
//【2】Task任务取消:同时我们也希望做一些清理的工作,也就是取消这个动作会触发一个任务。
static void Method2()
{
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();//取消任务
}
//【3】Task任务延时自动取消:比如我们请求一个远程接口,如果长时间没有返回数据,我们可以做一个时间限制,超时可以取消任务(比如微信红包退回)
static void Method3()
{
CancellationTokenSource cts = new CancellationTokenSource();
// CancellationTokenSource cts = new CancellationTokenSource(3000);
Task task = Task.Factory.StartNew(() =>
{
while (!cts.IsCancellationRequested)
{
Thread.Sleep(300);
Console.WriteLine($"子线程Id={Thread.CurrentThread.ManagedThreadId} {DateTime.Now.ToLongTimeString()}");
}
}, cts.Token);
//注册一个委托:这个委托将在任务取消的时候调用
cts.Token.Register(() =>
{
//在这个地方可以编写自己要处理的逻辑...
Console.WriteLine("任务取消,开始清理工作......");
Thread.Sleep(2000);
Console.WriteLine("任务取消,清理工作结束......");
});
cts.CancelAfter(3000); //3秒后自动取消
}
5、监视锁:Lock 限制线程个数的一把锁
//为什么要用锁?在多线程中,尤其是静态资源的访问,必然会有竞争
private static int nums = 0;
private static object myLock = new object();
static void Method1()
{
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类型。