异步编程:基于委托实现
1 声明委托
public delegate int MyCalculator(int num, int ms);
2 根据委托定义一个方法:
private int ExecuteTask(int num, int ms)
{
System.Threading.Thread.Sleep(ms);
return num * num;
}
3使用委托,创建委托变量(因为异步函数和回调函数都要用,所以定义成员变量)
private MyCalculator objMyCal = null;
初始化委托变量
public FrmCalllBack()
{
InitializeComponent();
//【3】初始化委托变量
this.objMyCal = new MyCalculator(ExecuteTask);
//也可以直接使用Lambda表达式
this.objMyCal = (num, ms) =>
{
System.Threading.Thread.Sleep(ms);
return num * num;
};
}
4 同时执行多个任务
//【4】同时执行多个任务
private void btnExec_Click(object sender, EventArgs e)
{
for (int i = 1; i < 11; i++)//产生10个任务
{
//开始异步执行,并封装回调函数 objMyCal.BeginInvoke(10 * i, 1000 * i, null, i);
objMyCal.BeginInvoke(10 * i, 1000 * i, MyCallBack, i);
//最后一个参数 i 给回调函数的字段AsyncState赋值,如果数据很多可以定义成类或结构
}
}
5 回调函数
private void MyCallBack(IAsyncResult result)
{
int res = objMyCal.EndInvoke(result);
//异步显示结果:result.AsyncState字段用来封装回调时自定义的参数,object类型
Console.WriteLine("第{0}个计算结果为:{1}", result.AsyncState.ToString(), res);
}
异步编程总结:
1. 异步编程是建立在委托基础上的一种编程方法。
2. 异步调用的每个方法都是在独立的线程中执行。因此,本质上就是一种多线程程序,也可以说是一种“简化版本”的多线程技术。
3. 比较适合在后台运行较为耗费时间的《简单任务》,并且任务要求相互独立,任务中不应该有代码直接访问可视化控件。
4. 如果后台任务要求必须按照特定顺序执行,或者必须访问共享资源,则异步编程不适合,而应该直接采用多线程开发技
C#多线程
进程:一个正在运行的程序就是一个进程。操作系统根据进程分配各种资源(内存。。。)
线程:操作系统为了提高效率会将一个进程分成多个线程,并按照线程来分配Cpu执行时间。
线程特点:在具有多个Cpu的计算机中,可以并行进程。
Thread类:表示托管线程,每个Thread对象都代表一个托管线程,每个托管线程会对应一个函数。
多线程基本使用:
//执行任务1
private void btnExecute1_Click(object sender, EventArgs e)
{
int a = 0;
// ThreadStart()方法的定义: public delegate void ThreadStart()
Thread objThread1 = new Thread(delegate()
{
for (int i = 1; i <= 20; i++)
{
Console.Write((a + i) + " ");
Thread.Sleep(500);
}
});
//Thread objThread1 = new Thread(() =>
//{
// for (int i = 1; i <= 20; i++)
// {
// Console.Write((a + i) + " ");
// Thread.Sleep(500);
// }
//});
objThread1.IsBackground = true;//设置为后台线程(通常都要这样设置)
objThread1.Start();
}
//执行任务2
private void btnExecute2_Click(object sender, EventArgs e)
{
Thread objThread2 = new Thread(() =>
{
for (int i = 1; i <= 50; i++)
{
Console.WriteLine("-------a" + i + "------- ");
Thread.Sleep(100);
}
});
objThread2.IsBackground = true;
objThread2.Start();
}
多线程使用其他线程资源
使用委托进行传值:
private void btnExecute1_Click(object sender, EventArgs e)
{
int a = 0;
//ThreadStart :表示一个委托
Thread objThread1 = new Thread(() =>
{
for (int i = 1; i <= 100; i++)
{
a += i;
if (this.lblResult1.InvokeRequired)//判断是否调动Invoke方法
{
//Invoke()方法的第一个参数是返回值为void的委托,第二个是给委托对应方法传递的参数
this.lblResult1.Invoke(new Action<string>(s => { this.lblResult1.Text = s; }), a.ToString());
}
Thread.Sleep(200);
}
});
objThread1.IsBackground = true;
objThread1.Start();
}
private void btnExecute2_Click(object sender, EventArgs e)
{
int a = 0;
Thread objThread2 = new Thread(() =>
{
for (int i = 1; i <= 100; i++)
{
a += i;
if (this.lblResult2.InvokeRequired)
{
this.lblResult1.Invoke(new Action<string>(s =>
{
this.lblResult2.Text = s;
}), a.ToString());
}
Thread.Sleep(200);
}
});
objThread2.IsBackground = true;
objThread2.Start();
}
多线程访问数据库:
private void btnExecute1_Click(object sender, EventArgs e)
{
Thread objThread1 = new Thread(() =>
{
Thread.Sleep(3000);
string classCount = DBUtility.SQLHelper.GetSingleResult("select count(*) from StudentClass").ToString();
this.lblResult1.Invoke(new Action<string>(c => { this.lblResult1.Text = c; }), classCount);
});
objThread1.IsBackground = true;
objThread1.Start();
}
private void btnExecute2_Click(object sender, EventArgs e)
{
Thread objThread2 = new Thread(() =>
{
Thread.Sleep(3000);
string stuCount = DBUtility.SQLHelper.GetSingleResult("select count(*) from Students").ToString();
this.lblResult2.Invoke(new Action<string>(s => { this.lblResult2.Text = s; }), stuCount);
});
objThread2.IsBackground = true;
objThread2.Start();
}
private void btnGetData_Click(object sender, EventArgs e)
{
Thread objThread3= new Thread(() =>
{
Thread.Sleep(2000);
//string stuCount = DBUtility.SQLHelper.GetSingleResult("select count(*) from Students").ToString();
//this.lblResult2.Invoke(new Action<string>(s => { this.lblResult2.Text = s; }), stuCount);
//访问数据库
DataSet ds = DBUtility.SQLHelper.GetDataSet("select * from StudentClass;select StudentName,Gender,PhoneNumber from Students");
DataTable dt1 = ds.Tables[0];
DataTable dt2 = ds.Tables[1];
this.dgv1.Invoke(new Action<DataTable>(c => { this.dgv1.DataSource = c; }), dt1);
Thread.Sleep(2000);
this.dgv2.Invoke(new Action<DataTable>(s => { this.dgv2.DataSource = s; }), dt2);
});
objThread3.IsBackground = true;
objThread3.Start();
}
进程相关概念和进程读取方法:
进程:每一个独立运行的程序都是进程。操作系统有《进程管理模块》管理同时运行的多个程序。
进程和线程:一个进程可以包含若干线程。
句柄:(Handle)句柄是一个标识符,用于代表操作系统中的各种资源。比如各种窗体、CDI绘制的对象、进程和线程对象、文件等都拥有句柄。
句柄使用1:即使进程退出,有很多时候操作系统仍然保持进程句柄。操作系统句柄有限,使用中要节省,比如打开文件后,使用完毕要及时关掉。
句柄使用2:句柄可以通过Process对象的Handle属性访问,比如进程退出的代码、时间等。
总结提高:拥有图形界面的程序都有一个主窗体,此窗体也对应一个句柄。当主窗体关闭的时候进程也关闭。主窗体一般由主线程负责创建。
获取本机所有进程:
private Process[] allProcess = null;//本机所有的进程
private void btnGetProcessess_Click(object sender, EventArgs e)
{
allProcess = Process.GetProcesses(); //获取本机所有进程
//循环显示
this.lbProcess.Items.Clear();
foreach (Process item in allProcess)
{
this.lbProcess.Items.Add(item.ProcessName);
}
}
#region 显示当前进程基本信息和所调用的模块信息
private void lbProcess_Click(object sender, EventArgs e)
{
if (this.lbProcess.Items.Count == 0) return;
else
this.lbModels.Items.Clear();
Process currentProcess = allProcess[this.lbProcess.SelectedIndex];//获取当前选中的进程
try
{
//获取当前进程所调用的模块(有些进程会拒绝访问)
ProcessModuleCollection models = currentProcess.Modules;
foreach (ProcessModule item in models)
{
this.lbModels.Items.Add(item.FileName);
}
//显示当前进程的基本信息
string info = @"进程的唯一标识符(Id):" + currentProcess.Id + "\r\n\r\n";
info += "关联进程的本机句柄(Handle):" + currentProcess.Handle + "\r\n\r\n";
info += "打开的句柄数(HandleCount):" + currentProcess.HandleCount + "\r\n\r\n";
info += "关联进程的基本优先级:" + currentProcess.BasePriority + "\r\n\r\n";
info += "进程启动的时间:" + currentProcess.StartTime;
this.txtProcessInfo.Text = info;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
#endregion
关闭和开启进程;
#region 关闭和开启进程
private void btnKill_Click(object sender, EventArgs e)
{
if (this.lbProcess.Items.Count == 0 || this.lbProcess.SelectedIndex == -1) return;
Process currentProcess = allProcess[this.lbProcess.SelectedIndex];//获取当前选中的进程
currentProcess.Kill();//立即停止关联的进程
currentProcess.Close();//释放进程资源
this.lbProcess.Items.RemoveAt(this.lbProcess.SelectedIndex);
this.lbModels.Items.Clear();
this.txtProcessInfo.Clear();
}
//开启一个新的进程(如:iexplore.exe;notepad.exe;D:\abc.pptx)
//在windows环境变量里面没有注册的,需要使用路径
private void btnStartnew_Click(object sender, EventArgs e)
{
Process.Start(this.txtProcessName.Text.Trim());
}
//打开IE并连接到百度堂官网
private void btnOpenIE_Click(object sender, EventArgs e)
{
Process.Start("iexplore.exe", "www.baidu.com");
}
#endregion
线程的安全:
资源争夺问题:
lock关键字可以确保一次只有一个线程调用它。
lock关键字可以简单理解成代码块的“锁”。
#region 线程同步(可实现线程安全)
static void Main(string[] args)
{
Thread.CurrentThread.Name = "--------------主线程--------------";
Program objDemo = new Program();
//创建一个新的线程
Thread objNewThread = new Thread(new ThreadStart(objDemo.ActionMethod));
objNewThread.Name = "子线程";
objNewThread.Start();//开始子线程,并为该线程执行ActionMethod
objDemo.ActionMethod();//让主线程执行ActionMethod
Console.ReadLine();
}
private int counter = 0;
//被线程调用的方法【未使用线程安全措施】
//private void ActionMethod()
//{
// for (int count = 1; count <= 10; count++)
// {
// counter += count;
// Console.WriteLine("{0}\t{1}", Thread.CurrentThread.Name, counter);
// }
//}
//使用线程安全方法
//private void ActionMethod()
//{
// //lock关键字可以确保一次只有一个线程调用它。
// //lock关键字可以简单理解成代码块的“锁”。
// lock (this)
// {
// for (int count = 1; count <= 10; count++)
// {
// counter += count;
// Console.WriteLine("{0}\t{1}", Thread.CurrentThread.Name, counter);
// }
// }
//}
//为了方便日常使用,提供了一个方法属性来实现同样的功能。
[MethodImpl(MethodImplOptions.Synchronized)]
private void ActionMethod()
{
for (int count = 1; count <= 10; count++)
{
counter += count;
Console.WriteLine("{0}\t{1}", Thread.CurrentThread.Name, counter);
}
}
#endregion