C#进程与多线程
一、进程Process
//获得当前程序中所有正在运行的进程 Process[] processes = Process.GetProcesses(); foreach (var item in processes) { //不试的不是爷们 //item.Kill(); Console.WriteLine(item); } //通过进程打开应用程序 Process.Start("calc"); Process.Start("mspaint"); Process.Start("notepad"); Process.Start("iexplore", "http://www.baidu.com"); //通过进程打开指定的文件 ProcessStartInfo psi = new ProcessStartInfo(@"C:\Users\liwen\Desktop\微信图片_20210104225821.png"); Process process = new Process() { StartInfo = psi }; process.Start();
二、线程Thread
1、ThreadStart是一个无参的、返回值为void的委托。委托定义如下:public delegate void ThreadStart()
通过Lambda表达式创建线程
Thread thread = new Thread(() => { Console.WriteLine("我是通过Lambda表达式创建的委托线程"); }); thread.Start();
2、ParameterizedThreadStart是一个有参的、返回值为void的委托,定义如下:public delegate void ParameterizedThreadStart(Object obj)
Thread thread = new Thread((e)=> { Console.WriteLine(e); }); thread.Start("这是一个有参数的委托");
3、线程的属性与方法
//获取正在运行的线程 Thread thread = Thread.CurrentThread; //设置线程的名字 thread.Name = "主线程"; //获取当前线程的唯一标识符 int id = thread.ManagedThreadId; //获取当前线程的状态 ThreadState state = thread.ThreadState; //获取当前线程的优先级 ThreadPriority priority = thread.Priority; string strMsg = string.Format("Thread ID:{0}\n" + "Thread Name:{1}\n" + "Thread State:{2}\n" + "Thread Priority:{3}\n", id, thread.Name, state, priority); Console.WriteLine(strMsg);
4、前台线程和后台线程-------前台线程:只有所有的前台线程都结束,应用程序才能结束。默认情况下创建的线程 都是前台线程
后台线程:只要所有的前台线程结束,后台线程自动结束。通过Thread.IsBackground设置后台线程。必须在调用Start方法之前设置线程的类型,否则一旦线程运行,将无法改变其类型。
static void Main(string[] args) { //演示前台、后台线程 BackGroundTest background = new BackGroundTest(10); //创建前台线程 Thread fThread = new Thread(new ThreadStart(background.RunLoop)); //给线程命名 fThread.Name = "前台线程"; BackGroundTest background1 = new BackGroundTest(20); //创建后台线程 Thread bThread = new Thread(new ThreadStart(background1.RunLoop)); bThread.Name = "后台线程"; //设置为后台线程 bThread.IsBackground = true; //启动线程 fThread.Start(); bThread.Start(); } class BackGroundTest { private int Count; public BackGroundTest(int count) { this.Count = count; } public void RunLoop() { //获取当前线程的名称 string threadName = Thread.CurrentThread.Name; for (int i = 0; i < Count; i++) { Console.WriteLine("{0}计数:{1}", threadName, i.ToString()); //线程休眠500毫秒 Thread.Sleep(1000); } Console.WriteLine("{0}完成计数", threadName); } }
运行结果:前台线程执行完,后台线程未执行完,程序自动结束。把bThread.IsBackground = true注释掉,运行结果:主线程执行完毕后(Main函数),程序并未结束,而是要等所有的前台线程结束以后才会结束
后台线程一般用于处理不重要的事情,应用程序结束时,后台线程是否执行完成对整个应用程序没有影响。如果要执行的事情很重要,需要将线程设置为前台线程。
5、线程同步-----所谓同步:是指在某一时刻只有一个线程可以访问变量。
static void Main(string[] args) { BookShop book = new BookShop(); //创建两个线程同时访问Sale方法 Thread t1 = new Thread(new ThreadStart(book.Sale)); Thread t2 = new Thread(new ThreadStart(book.Sale)); //启动线程 t1.Start(); t2.Start(); Console.ReadKey(); } class BookShop { //剩余图书数量 public int num = 1; public void Sale() { //使用lock关键字解决线程同步问题 lock (this) { int tmp = num; if (tmp > 0)//判断是否有书,如果有就可以卖 { Thread.Sleep(1000); num -= 1; Console.WriteLine("售出一本图书,还剩余{0}本", num); } else { Console.WriteLine("没有了"); } } } }
6、跨线程访问-----在窗体的加载事件中,将C#内置控件(Control)类的CheckForIllegalCrossThreadCalls属性设置为false,屏蔽掉C#编译器对跨线程调用的检查。
private void Form1_Load(object sender, EventArgs e) { //取消跨线程的访问 Control.CheckForIllegalCrossThreadCalls = false; }
使用回调机制-----C#的方法回调机制,也是建立在委托基础上的,下面给出它的典型实现过程
//定义回调 private delegate void setTextValueCallBack(int value); public void ThreadCallBackT() { //实例化回调 var setCallBack = new setTextValueCallBack((e) => { Console.WriteLine(e); }); //创建一个线程去执行这个方法:创建的线程默认是前台线程 Thread thread = new Thread(new ThreadStart(()=> { for (int i = 0; i < 100; i++) { //使用回调 setCallBack.Invoke(i); } })); //Start方法标记这个线程就绪了,可以随时被执行,具体什么时候执行这个线程,由CPU决定 //将线程设置为后台线程 thread.IsBackground = true; thread.Start(); }
二、关于线程同步
1、文件操作的帮助类
class TextHelper { static ReaderWriterLockSlim logLock = new ReaderWriterLockSlim(); /// <summary> /// 读取文件 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="path"></param> /// <returns></returns> public static T ReadSerialize<T>(string path) { try { logLock.EnterReadLock(); if (File.Exists(path)) { using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read)) { BinaryFormatter formatter = new BinaryFormatter(); var result = formatter.Deserialize(stream); stream.Close(); return (T)result; } } return default; } catch (IOException ex) { Console.WriteLine(ex.Message); return default; } finally { logLock.ExitReadLock(); } } /// <summary> /// 写文件 /// </summary> /// <param name="path"></param> /// <param name="value"></param> public static void WriteSerialize(string path, object value) { try { logLock.EnterWriteLock(); using (FileStream stream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write)) { BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(stream, value); stream.Close(); } } catch (IOException ex) { Console.WriteLine(ex.Message); } finally { logLock.ExitWriteLock(); } } }
2、序列化数据类
[Serializable] class UserInfo { public string Name; public string Password; public int Age; public override string ToString() { return Name + "--->" + Password + "--->" + Age; } }
3、线程同步的测试
public static void TestMethod() { string fileName = "Test.txt"; for (int i = 1; i <= 10; i++) { var userInfo = new UserInfo() { Name = "我", Age = i, Password = "Pwd" + i }; var task1 = Task.Factory.StartNew((obj) => { TextHelper.WriteSerialize(fileName, obj); }, userInfo); var task2 = Task.Factory.StartNew(() => { return TextHelper.ReadSerialize<UserInfo>(fileName); }); Task.WaitAll(task1, task2); Console.WriteLine(task2.Result); } Console.Read(); }
以上方法测试的结果不稳定,会出现多种结果
线程同步的方式-1-------下面方法可以得到想要的结果
public static void TestMethod3() { string fileName = "Test.txt"; for (int i = 1; i <= 10; i++) { var userInfo = new UserInfo() { Name = "我", Age = i, Password = "PWD" + i }; Thread t0 = new Thread((info) => { TextHelper.WriteSerialize(fileName, info); }); Thread t1 = new Thread(new ThreadStart(() => { Console.WriteLine(TextHelper.ReadSerialize<UserInfo>(fileName)); })); t0.Start(userInfo); t1.Start(); t0.Join(); t1.Join(); } Console.Read(); }
线程同步的方式-2-
新建一个线程同步的帮助类
class UserInfoHelper { //将信号状态设置为非终止状态,使用手动重置 static EventWaitHandle myEventWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset); string fileName = "Test.txt"; public UserInfo ReadThread() { //阻塞当前线程,直到收到信号 myEventWaitHandle.WaitOne(); var userInfo= TextHelper.ReadSerialize<UserInfo>(fileName); return userInfo; } public void WriteThread(object userInfo) { //允许其他需要等待的线程阻塞 myEventWaitHandle.Reset(); TextHelper.WriteSerialize(fileName, userInfo); //允许其他等待的线程继续 myEventWaitHandle.Set(); } }
调用方法测试
public static void TestMethod2() { UserInfoHelper infoHelper = new UserInfoHelper(); for (int i = 1; i <= 10; i++) { var userInfo = new UserInfo() { Name = "我", Age = i, Password = "PWD" + i }; Thread t0 = new Thread((info) => { infoHelper.WriteThread(info); }); Thread t1 = new Thread(new ThreadStart(() => { Console.WriteLine(infoHelper.ReadThread()); })); t0.Start(userInfo); t1.Start(); } Console.Read(); }
输出结果如下,所有写操作完成后,才逐步进行读操作;;;如果加入 t0.Join(); t1.Join();得到想要的结果
线程同步的方式-3 该方式效率最高
public static void TestMain() { UserInfoHelper infoHelper = new UserInfoHelper(); for (int i = 1; i <= 10; i++) { var userInfo = new UserInfo() { Name = "我", Age = i, Password = "PWD" + i }; var task1 = Task.Factory.StartNew((obj) => { infoHelper.WriteThread(obj); Thread.Sleep(3000); }, userInfo); var task2 = Task.Factory.StartNew(() => { return infoHelper.ReadThread(); }); //Task.WaitAll(task2);//最好调用此方法进行等待,防止出错 Task.WhenAll(); Console.WriteLine(task2.Result); } Console.ReadLine(); }
一个线程的封装案列
class ThreadHelper { public static void TestDemo() { ThreadHelper threadHelper = new ThreadHelper(); { void threadStart() { Thread.Sleep(2000); Console.WriteLine("Thread第一个执行的委托方法"); }; Action action = () => { Console.WriteLine("第二个执行的委托方法"); }; threadHelper.ThreadWithCallBack(threadStart, action); } { //定义一个返回值类型为int的无参数委托,在新线程中调用 Func<int> func = () => { Thread.Sleep(2000); return DateTime.Now.Day; }; //将func作为参数传入扩展方法执行,并用变量接收返回的委托类型 Func<int> funResult = threadHelper.ThreadWithReturn(func); //执行委托接收返回值 int iResult = funResult.Invoke(); Console.WriteLine(iResult); } } /// <summary> /// 扩展封装Thread /// </summary> /// <param name="threadStart">开启新线程需要执行的任务</param> /// <param name="actionCallback">任务执行后紧接着需要执行的任务</param> private void ThreadWithCallBack(ThreadStart threadStart, Action actionCallback) { ThreadStart threadStart1 = new ThreadStart(() => { threadStart.Invoke(); actionCallback.Invoke(); }); Thread thread = new Thread(threadStart1); thread.Start(); } /// <summary> /// 扩展封装Thread,有返回值,需要获取结果的时候运行委托,卡界面 /// </summary> /// <typeparam name="T">委托执行后的返回值</typeparam> /// <param name="func">开启新线程需要执行的任务</param> /// <returns></returns> private Func<T> ThreadWithReturn<T>(Func<T> func) { //指定泛型默认值 T t = default(T); ThreadStart threadStart = new ThreadStart(() => { t = func.Invoke(); }); Thread thread = new Thread(threadStart); thread.Start(); return new Func<T>(() => { thread.Join(); return t; }); } }
参考链接 https://www.cnblogs.com/dotnet261010/p/6159984.html