1. Thread
Thread默认开启前台线程。 其他的开启方式默认开启后台线程。
-
首先导入命名空间:
System.Threading
-
实例化一个Thread对象
Thread newThread = new Thread(AMethod);
// AMethod,为该线程将要执行的任务。
- 启动线程
若要开始执行线程,可使用Start方法
newThread.Start();
- 停止线程
newThread.Abort();
Thread
线程的多种常用方法列表
方法 | 操作 |
---|---|
Start | 使线程开始运行 |
Sleep | 使线程暂停指定的一段时间。 |
Suspend | 在线程到达安全点时,使其暂停。 |
Abort | 在线程到达安全点时,使其停止。 |
Resume | 重新启动挂起的线程 |
Join | 使当前线程一直等到另一线程完成。 在与超时值一起使用时,如果该线程在分配的时间内完成,此方法将返回 True。 |
**安全点: **
安全点是指代码中公共语言运行时可以安全地执行自动“垃圾回收”的位置。垃圾回收是指释放不再使用的变量并回收内存的过程。 调用线程的 Abort 或 Suspend 方法时,公共语言运行时将对代码进行分析,确定让线程停止运行的适当位置。
Thread
的相关属性:
属性 | 值 |
---|---|
IsAlive | 如果线程处于活动状态,则包含值 True。 |
IsBackground | 获取或设置一个布尔值,该值表示一个线程是否是,或是否应当是后台线程。 后台线程与前台线程类似,但后台线程不阻止进程停止。 一旦某个进程的所有前台线程都停止,公共语言运行时就会对仍处于活动状态的后台线程调用 Abort 方法,从而结束该进程。 |
Name | 获取或设置线程的名称。 通常用于在调试时发现各个线程。 |
Priority | 获取或设置操作系统用于确定线程调度优先顺序的值。 |
ApartmentState | 获取或设置用于特定线程的线程模型。 线程模型在线程调用非托管代码时很重要。 |
ThreadState | 包含描述线程状态的值。 |
线程优先级
每个线程都有一个优先级属性,用于确定其执行所占用的处理器时间片大小。 操作系统为高优先级线程分配较长的时间段,并为低优先级线程分配较短的时间段。 新创建的线程具有值 Normal
,但可以将 Priority 属性更改为 ThreadPriority 枚举中的任何值
。
2. 通过委托来开启线程
通过委托,调用BeginInvoke
方法来开启一条线程,去执行该委托所指向的方法。
// 线程执行该方法
static string test1(int x, int y)
{
Console.WriteLine(x + y);
return x + "+"+ y;
}
static void Main(string[] args)
{
Func<int, int, string> fun1 = test1;
// 通过委托来开启线程执行。 倒数第二个参数是,线程执行完毕之后的回调方法, 倒数第一个参数是,执行完之后,携带的参数。
fun1.BeginInvoke(10, 20, test1CallBack, fun1);
}
// 通过委托来传递回调函数,会自动将该线程的执行结果 传递过来
static void test1CallBack(IAsyncResult ar)
{
//AsyncState 获取所携带的参数,在这里就是上面fun1.BeginInvoke(10, 20, test1CallBack, fun1); 这行代码中,所传递的fun1
Func<int, int, string> fun1 = ar.AsyncState as Func<int, int, string>;
// 获取委托所指向的方法的执行结果。
string result = fun1.EndInvoke(ar);
Console.WriteLine("这就是结果" + result);
}
3. 线程池
static void ThreadFunc3()
{
Console.WriteLine("--------");
// 调用线程池来执行任务。 每调用一次,就会开启一条线程来执行该任务。
ThreadPool.QueueUserWorkItem(test3);
ThreadPool.QueueUserWorkItem(test3);
ThreadPool.QueueUserWorkItem(test3);
ThreadPool.QueueUserWorkItem(test3);
ThreadPool.QueueUserWorkItem(test3);
ThreadPool.QueueUserWorkItem(test3);
ThreadPool.QueueUserWorkItem(test3);
ThreadPool.QueueUserWorkItem(test3);
Console.WriteLine("========");
Thread.Sleep(100);
}
// 线程池调用的方法,需要一个object类型的参数方法,且不能有返回值
static void test3(object obj)
{
Thread.CurrentThread.IsBackground = false;
Console.WriteLine("asdas");
}
4. Task 开启
static void ThreadMethod()
{
Console.WriteLine("ThreadMethod Begin" + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000);
Console.WriteLine("Thread End");
}
static void continueTask(Task t)
{
Console.WriteLine("task is continue" + t.Id);
Thread.Sleep(3000);
Console.WriteLine("task is complete");
}
static void Main(string[] args)
{
// 第一种方式开启
//Task t = new Task(ThreadMethod);
//t.Start();
第二种方式开启
//TaskFactory tf = new TaskFactory();
//tf.StartNew(ThreadMethod);
Task t1 = new Task(ThreadMethod);
Task t2 = t1.ContinueWith(continueTask);
t1.Start(); // 开启任务 t1, 当t1执行完毕的时候,会执行t2的任务
Console.ReadKey();
}
5. 线程使用问题
- 资源竞争
多个线程访问同一个资源,对同一个资源做修改,会引发数据混乱。 - 死锁
多把锁。互相牵制。导致线程都互相等待解锁。 - 案例:
- 银行存取款
- 多窗口同时卖票
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace TestProject
{
class SaleTicket
{
private Thread thread1;
private Thread thread2;
private Thread thread3;
private int ticketCount = 1000;
public SaleTicket()
{
thread1 = new Thread(Sale);
thread2 = new Thread(Sale);
thread3 = new Thread(Sale);
}
public void Sale()
{
while(true)
{
lock(this)
{
if (ticketCount > 0)
{
ticketCount -= 1;
Console.WriteLine(Thread.CurrentThread.Name + "卖了一张票,剩余" + ticketCount);
}
else
{
Console.WriteLine(Thread.CurrentThread.Name + ":票卖完了");
break;
}
}
}
}
public void StartSaleTicket()
{
thread1.Start();
thread2.Start();
thread3.Start();
}
}
}