一、进程和线程理论知识
进程:正在被操作系统运行的程序,一个进程中包含多个线程。
线程:CPU执行的基本逻辑单元
1、进程类
进程类:Process 操作进程 用于启动、停止、监控和管理进程
GetProcesses():获取所有正在运行的进程
StartInfo:进程的启动信息(启动哪个进程)
2、线程
(1)Thread 类
Thread 类是用于控制线程的基础类,它存在于 System.Threading 命名空间。通过 Thread 可以控制当前应用程序域中线程的创建、挂起、停止、销毁。
属性 | 说明 |
Name | 获取或设置线程的名称 |
ThreadState | 获取当前线程的运行状态 |
CurrentThread | 获取当前正在运行的线程 |
Priority | 获取或设置线程调度的优先级别 |
方法 | 说明 |
Start | 启动执行线程 |
Suspend | 挂起线程,如果线程已挂起,则方法无效 |
Resume | 恢复被挂起的线程 |
Join | 阻塞线程,直到线程终止或任务执行完毕 |
Abort | 终止线程运行 |
(2)单线程:
带来的问题:点击按钮,循环打印1-10000,界面会陷入假死状态
每运行一个程序,就会分配一个主线程工作
解决方案:创建一个子线程去执行这个方法
所有的前台线程都关闭,程序才会关闭;前台线程一旦关闭,后台线程自动关闭,所以上述应改成后台线程
th.IsBackground=true;
eg1:单线程应用程序 ,委托打印1-10
static void Main(string[] args)
{
//创建不带参数方法的委托对象
ThreadStart ts = new ThreadStart(OutPut);
Thread thread = new Thread(ts);
thread.Start();
Console.ReadKey(true);//按任意键退出
}
static void OutPut()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i);
}
}
例:将i值显示到TextBox中,会报异常:从不是创建它的线程上访问它
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//ThreadStart te = new ThreadStart(Test);
Thread th = new Thread(Test);
th.Start(); //启动线程
}
public void Test()
{
string str = "";
for (int i = 0; i <= 1000000; i++)
{
str += i.ToString();
}
textBox1.Text = str;
}
}
.net中不允许跨线程调用,
解决方案:Control.CheckForIllegalCrossThreadCalls = false;
问题又来了,中途点击关闭按钮的时候,程序会抛异常,因为主线程已经关闭,子线程调用不到主线程上的控件,解决方案:
(3)多线程:
可以让程序同时做多个事儿 线程创建步骤。
eg2:多线程应用程序
static void Main(string[] args)
{
Thread.CurrentThread.Name = "主线程";
//创建第1个子线程
ThreadStart ts = new ThreadStart(Output);
Thread thread1 = new Thread(ts);
thread1.Name = "子线程1";
thread1.Start();
//创建第二个子线程
Thread thread2 = new Thread(ts);
thread2.Name = "子线程2";
thread2.Start();
//主线程调用Output方法
Program.Output();
}
输出结果顺序混乱
原因在于CPU在执行任务时是分时执行的;并且与双核(多核)CPU有关。
thread1.Name = "子线程1";
//启动子线程
();
//创建第二个子thread1.Start();
thread1.Join线程
Thread thread2 = new Thread(ts);
thread2.Name = "子线程2";
thread2.Start();
thread2.Join();
//主线程调用Output方法
Program.Output();
Thread1.Jion():Thread1执行,其他线程等待
线程按顺序执行
3、使用同步技术解决多线程资源共享
(1)线程的挂起、恢复
thread1.Suspend();//挂起线程
thread1.Resume(); //恢复线程
微软已标记过时,但还可以用!!!!
Thread.CurrentThread.Name = "主线程";
//创建第1个子线程
ThreadStart ts = new ThreadStart(Output);
Thread thread1 = new Thread(ts);
thread1.Name = "子线程1";
//启动子线程
thread1.Start();
if (thread1.ThreadState == ThreadState.Running)
{
thread1.Suspend();//挂起线程
}
Program.Output();
if (thread1.ThreadState == ThreadState.Suspended)
{
thread1.Resume(); //恢复线程
}
Console.ReadLine();
}
public static void Output()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("线程:{0}, i的值为:{1}", Thread.CurrentThread.Name, i);
}
}
}
(2)线程的睡眠与终止:
Thread.Sleep(3000);//使主线程睡眠3秒
static void Main(string[] args)
{
Thread.CurrentThread.Name = "主线程";
//创建第1个子线程
ThreadStart ts = new ThreadStart(Output);
Thread thread1 = new Thread(ts);
thread1.Name = "子线程1";
thread1.Start();
Thread.Sleep(3000);//使主线程睡眠3秒
//主线程调用Output方法
Program.Output();
Console.ReadKey(true);
}
public static void Output()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("线程:{0}, i的值为:{1}", Thread.CurrentThread.Name, i);
}
}
Thread.CurrentThread.Abort();//终止
static void Main(string[] args)
{
Thread.CurrentThread.Name = "主线程";
//创建第1个子线程
ThreadStart ts = new ThreadStart(Output);
Thread thread1 = new Thread(ts);
thread1.Name = "子线程1";
//启动子线程
thread1.Start();
thread1.Join();
Console.ReadKey(true);
}
public static void Output()
{
try
{
for (int i = 0; i < 10000; i++)
{
if (i > 5)
Thread.CurrentThread.Abort();
Console.WriteLine("线程:{0}, i的值为:{1}", Thread.CurrentThread.Name, i);
}
}
catch (ThreadAbortException ex)
{
Console.WriteLine(ex.Message);
}
}
(3)线程的调度
Thread.CurrentThread .Priority = ThreadPriority.Highest;//优先级最高
成员 | 说明 |
Highest | 优先级最高 |
AboveNormal | 优先级次之 |
Normal | 优先级在AboveNormal之下,线程的默认优先级 |
BelowNormal | 优先级在Normal之下 |
Lowest | 优先级最低 |
总结:
当线程需要启动时 th.Start(); //启动线程
当线程需要挂起时 th.Suspend();//挂起线程
当线程需要继续开始时 th.Resume();//继续线程
当线程需要停止时(下班了) th.Abort();//中止线程
4.线程池ThreadPool类的使用
需要调用ThreadPool类中的QueueUserWorkItem 方法:
ThreadPool.QueueUserWorkItem(WaitCallback waitcallback)
WaitCallback:代表线程要执行的方法,是个委托类型
注意:
- 不能给线程池中的线程设置优先级或线程名称
- 线程池中的线程适合用于时间较短的任务
eg:“费波那契”
编写一个计算前100个“费波那契”数字输出的双线程应用程序,程序中这两个线程都可以完成“费波那契”数字的计算和显示。将这两个线程赋予不同的执行等待时间,同时应能够控制线程的启动、挂起、唤醒和终止。
“费波那契”数字排列格式为:后一个数字是前两个数字之和,例如1、1、2、3、5、8、13、21、34…。
FebroSeries类:
class FebroSeries
{
public long firstValue; //上前一个数字
public long secondValue;//前一个数字
private long currentValue;//当前数字值
public FebroSeries()
{
firstValue = 1;
secondValue = 1;
currentValue = 0;
}
/// <summary>
/// 计算新数值
/// </summary>
/// <returns></returns>
public long GetResult()
{
currentValue = firstValue + secondValue;
firstValue = secondValue;
secondValue = currentValue;
return currentValue;
}
}
FebroForm:
public partial class FebroForm : Form
{
//声明线程
Thread fastThread = null;
Thread slowThread = null;
public FebroForm()
{
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;
}
private void btnStart_Click(object sender, EventArgs e)
{
//创建快线程,该线程执行FastFebro方法
fastThread = new Thread(new ThreadStart(FastFebro));
//创建慢线程,该线程执行SlowFebro方法
slowThread = new Thread(new ThreadStart(SlowFebro));
//启动线程
fastThread.Start();
slowThread.Start();
//控制按钮可用状态
btnStart.Enabled = false; //开始按钮不可用
btnContinue.Enabled = false;//继续按钮不可用
btnPause.Enabled = true;//暂停按钮可用
btnStop.Enabled = true;//停止按钮可用
}
private void FastFebro()
{
FebroSeries febro = new FebroSeries();
lstFebro1.Items.Add(febro.firstValue);
lstFebro1.Items.Add(febro.secondValue);
for (int i = 0; i < 200; i++)
{
//向lstFebro1中添加新的"费波那契"数字
lstFebro1.Items.Add(febro.GetResult());
Thread.Sleep(1000);
}
}
private void SlowFebro()
{
FebroSeries febro = new FebroSeries();
lstFebro2.Items.Add(febro.firstValue);
lstFebro2.Items.Add(febro.secondValue);
for (int i = 0; i < 200; i++)
{
//向lstFebro1中添加新的"费波那契"数字
lstFebro2.Items.Add(febro.GetResult());
Thread.Sleep(3000);
}
}
private void btnStop_Click(object sender, EventArgs e)
{
if ((fastThread.ThreadState == ThreadState.Running || fastThread.ThreadState == ThreadState.WaitSleepJoin) && (slowThread.ThreadState == ThreadState.Running || slowThread.ThreadState == ThreadState.WaitSleepJoin))
{
//线程挂起
fastThread.Suspend();
slowThread.Suspend();
btnContinue.Enabled = true;//继续按钮可用
btnPause.Enabled = false;//暂停按钮可用
btnStart.Enabled = false;//开始按钮可用
btnStop.Enabled = false;//停止按钮可用
}
}
private void btnContinue_Click(object sender, EventArgs e)
{
if (fastThread.ThreadState == ThreadState.Suspended ||
fastThread.ThreadState == ThreadState.SuspendRequested ||
slowThread.ThreadState == ThreadState.Suspended ||
slowThread.ThreadState == ThreadState.SuspendRequested)
{
try
{
//唤醒线程
fastThread.Resume();
slowThread.Resume();
btnContinue.Enabled = false;//继续按钮不可用
btnPause.Enabled = true;//暂停按钮可用
btnStop.Enabled = true;//停止按钮可用
btnStart.Enabled = false;//开始按钮可用
}
catch (ThreadStateException msg)
{
MessageBox.Show(msg.ToString(), "Exception");
}
}
}
private void btnEnd_Click(object sender, EventArgs e)
{
//线程终止
fastThread.Abort();
slowThread.Abort();
btnStart.Enabled = true;
btnContinue.Enabled = false;
btnPause.Enabled = false;
btnStop.Enabled = false;
}
private void FebroForm_FormClosed(object sender, FormClosedEventArgs e)
{
if (fastThread == null && slowThread == null)
{
Application.Exit();
}
else
{
fastThread.Abort();
slowThread.Abort();
}
}
}
例:播放音乐
例:摇奖机