C#多线程实例汇总

C#多线程实例汇总

我汇总了一下关于C#多线程的使用方法,包括一些简单的锁和线程池的常用方法,虽然古老了一些,但是在一些老的项目中还是能经常用到的,多线程和异步有点儿小区别,分两篇文章来汇总,下一篇我再总结一下关于C#异步的新旧使用方法。

文章目录

      • C#多线程实例汇总
        • 1. 显示线程信息
        • 2. 线程的基本使用
        • 3. 线程同步
        • 4. 线程池常用实例
        • 5. 前台线程与后台线程
        • 6. 综合实例

1. 显示线程信息

Thread thread0 = Thread.CurrentThread;
thread0.Name = "Main Thread";
string threadMessage = string.Format("Thread ID:{0}\n Current AppDomainId:{1}\n " + 
		"Current ContextId:{2}\n Thread Name:{3}\n" + 
		"Thread State:{4}\n Thread Priority:{5}\n", 
		thread0.ManagedThreadId, Thread.GetDomainID(), Thread.CurrentContext.ContextID,thread0.Name, thread0.ThreadState, thread0.Priority);
		Console.WriteLine(threadMessage);

2. 线程的基本使用

// 无参数线程
Thread thread1 = new Thread(new ThreadStart(ShowMessage));
thread1.Start();

// 有参数线程
// 有参数委托线程
//Thread thread2 = new Thread(new ParameterizedThreadStart(ShowMessage));
Thread thread2 = new Thread(ShowMessage1);
Person person = new Person();
person.Name = "Jack";
person.Age = 21;
thread2.Start(person);
//构造有参数线程类
MyThread myThread = new MyThread("hello world");
Thread thread = new Thread(myThread.ThreadMain);
thread.Start();

public static void ShowMessage()
{
     
	string message = string.Format("Async threadId is :{0}", Thread.CurrentThread.ManagedThreadId);
	Console.WriteLine(message);
	for (int n = 0; n < 10; n++)
{
     
	Thread.Sleep(300);
	Console.WriteLine("The number is:" + n.ToString());
}
}

// 构造有参数线程类
public class MyThread
{
     
	private string data;
	public MyThread(string data)
	{
     
		this.data = data;
	}
	public void ThreadMain()
	{
     
		Console.WriteLine("Running in a thread,data: {0}", data);
	}
}

public class Person
{
     
	public string Name {
      get; set; }
	public int Age {
      get; set; }
}
public static void ShowMessage(object _person)
{
     
	if (_person != null)
	{
     
		Person person = (Person)_person;
		string message = string.Format("\n{0}'s age is {1}!\nAsync threadId is:{2}", person.Name, person.Age, Thread.CurrentThread.ManagedThreadId);
		Console.WriteLine(message);
	}
	for (int n = 0; n < 10; n++)
	{
     
		Thread.Sleep(300);
		Console.WriteLine("The number is:" + n.ToString());
	}
}

3. 线程同步

为同步访问变量,使用了一个C#语言的关键字Lock,它可以把一段代码定义为互斥段,互斥段在一个时刻内只允许一个线程进入执行,实际上是Monitor.Enter(obj),Monitor.Exit(obj)的语法糖。

obj代表你希望锁定的对象,注意一下几点:

  • lock不能锁定空值 ,因为Null是不需要被释放的。
  • 不能锁定string类型 ,虽然它也是引用类型的。因为字符串类型被CLR“暂留”,这意味着整个程序中任何给定字符串都只有一个实例,具有相同内容的字符串上放置了锁,就将锁定应用程序中该字符串的所有实例。
  • 值类型不能被lock ,每次装箱后的对象都不一样 ,锁定时会报错
  • 避免锁定public类型 如果该实例可以被公开访问,则 lock(this)可能会有问题,因为不受控制的代码也可能会锁定该对象。 推荐使用 private static readonly类型的对象,readonly是为了避免lock的代码块中修改对象,造成对象改变后锁失效
BookShop book = new BookShop();
Thread t1 = new Thread(book.Sale);
Thread t2 = new Thread(book.Sale);
t1.Start();
t2.Start();

class BookShop
{
     
	//剩余图书数量
	public int num = 1;
	private static readonly object locker = new object();
	public void Sale()
	{
     
		lock (locker)
		{
     
			int tmp = num;
			if (tmp > 0)//判断是否有书,如果有就可以卖
			{
     
				Thread.Sleep(1000);
				num -= 1;
				Console.WriteLine("售出一本图书,还剩余{0}本", num);
			}
			else
			{
     
				Console.WriteLine("没有了");
			}
		}
	}
}

4. 线程池常用实例

CLR线程池分为工作者线程(workerThreads)与I/O线程 (completionPortThreads) 两种,工作者线程是主要用作管理CLR内部对象的运作,I/O(Input/Output) 线程顾名思义是用于与外部系统交换信息。
通过

  • ThreadPool.GetMax(out int workerThreads,out int completionPortThreads )
  • ThreadPool.SetMax( int workerThreads, int completionPortThreads)

两个方法可以分别读取和设置CLR线程池中工作者线程与I/O线程的最大线程数。
在Framework2.0中最大线程默认为25CPU数,在Framewok3.0、4.0中最大线程数默认为250CPU数,在近年 I3,I5,I7 CPU出现后,线程池的最大值一般默认为1000、2000。若想测试线程池中有多少的线程正在投入使用,可以通过ThreadPool.GetAvailableThreads( out int workerThreads,out int completionPortThreads ) 方法。
使用CLR线程池的工作者线程一般有两种方式,一是直接通过 ThreadPool.QueueUserWorkItem() 方法,二是通过委托。

// 通过QueueUserWorkItem启动工作者线程
// 把CLR线程池的最大值设置为1000
ThreadPool.SetMaxThreads(1000, 1000);
// 显示主线程启动时线程池信息
ThreadMessage("Start");
// 启动工作者线程
ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncCallback1));
ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncCallback2), "Hello Elva");

for (int i = 1; i <= 10; i++)
{
     
	//ThreadPool执行任务
	ThreadPool.QueueUserWorkItem(
			new WaitCallback(
				(obj) => 
				{
     
				Console.WriteLine($"第{obj}个执行任务");
				}
			), 
	i);
}

static void AsyncCallback1(object state)
{
     
	Thread.Sleep(200);
	ThreadMessage("AsyncCallback");
	Console.WriteLine("Async thread do work!");
}
static void AsyncCallback2(object state)
{
     
	Thread.Sleep(200);
	ThreadMessage("AsyncCallback");
	string data = (string)state;
	Console.WriteLine("Async thread do work!\n" + data);
}
//显示线程现状
static void ThreadMessage(string data)
{
     
	string message = string.Format("{0}\n  CurrentThreadId is {1}",data, Thread.CurrentThread.ManagedThreadId);
	Console.WriteLine(message);
}

5. 前台线程与后台线程

使用Thread.Start()启动的线程默认为前台线程,而系统必须等待所有前台线程运行结束后,
应用程序域才会自动卸载。线程Thread有一个属性IsBackground,通过把此属性设置为true,
就可以把线程设置为后台线程!这时应用程序域将在主线程完成时就被卸载,而不会等待异步线程的运行。

Thread thread3 = new Thread(new ThreadStart(ShowMessage));
thread3.IsBackground = true;
thread3.Start();

Thread thread4 = new Thread(new ThreadStart(AsyncThread));
thread4.IsBackground = true;
thread4.Start();
//阻塞主线程
thread4.Join();

6. 综合实例


public partial class MainForm : Form
{
     
	
	Thread thread;
	int index = 0;
	public MainForm()
	{
     
		InitializeComponent();
	}
	//启动按钮
	private void startBtn_Click(object sender, EventArgs e)
	{
     
		//创建一个线程,每秒在textbox中追加一下执行次数
		if (thread==null)
		{
     
			thread = new Thread(() =>
			{
     
				while (true)
				{
     
					index++;
					try
					{
     
						Thread.Sleep(1000);
						textBox1.Invoke(new Action(() =>
						{
     
							textBox1.AppendText($"第{index}次,");
						}));
					}
					catch (Exception ex) {
      MessageBox.Show(ex.ToString()); }
				}
			});
			//启动线程
			thread.Start();
		}
	}
	//挂起按钮
	private void suspendBtn_Click(object sender, EventArgs e)
	{
     
		if (thread != null && thread.ThreadState==ThreadState.Running || thread.ThreadState==ThreadState.WaitSleepJoin)
		{
     
			thread.Suspend();
		}
	}
	//继续运行挂起的线程
	private void ResumeBtn_Click(object sender, EventArgs e)
	{
     
		if (thread!=null && thread.ThreadState==ThreadState.Suspended)
		{
     
			thread.Resume();
		}
	}
	//interrupt会报一个异常,并中断处于WaitSleepJoin状态的线程
	private void InterruptBtn_Click(object sender, EventArgs e)
	{
     
		if (thread != null && thread.ThreadState==ThreadState.WaitSleepJoin)
		{
     
			thread.Interrupt(); 
		}
	}
	//abort会报一个异常,并销毁线程
	private void AbortBtn_Click(object sender, EventArgs e)
	{
     
		if (thread != null)
		{
     
			thread.Abort();
		}
	}
	//定时器,刷新显示线程状态
	private void timer1_Tick(object sender, EventArgs e)
	{
     
		if (thread!=null)
		{
     
			txtStatus.Text = thread.ThreadState.ToString();
		}
	}
	//窗体加载
	private void Form1_Load(object sender, EventArgs e)
	{
     
		timer1.Interval = 100;
		timer1.Enabled = true;
	}
	//窗口关闭时,关闭进程
	private void Form1_FormClosing(object sender, FormClosingEventArgs e)
	{
     
		System.Diagnostics.Process[] processes =
		 System.Diagnostics.Process.GetProcessesByName("ThreadForm");
		foreach (var item in processes)
		{
     
			item.Kill();
		}
	}
}

你可能感兴趣的:(.NET,c#,多线程,并发编程)