C#多线程实例汇总

C#多线程实例汇总

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

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();
		}
	}
}
  • 3
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
多个C#多线程开发实例 using System; using System.Collections.Generic; using System.Text; namespace Example20 { class Program { class Class1 : IDisposable { //析构函数,编译后变成 protected void Finalize(),GC会在回收对象前会调用调用该方法 ~Class1() { Dispose(false); } //通过实现该接口,客户可以显式地释放对象,而不需要等待GC来释放资源,据说那样会降低效率 void IDisposable.Dispose() { Dispose(true); } //将释放非托管资源设计成一个虚函数,提供在继承类中释放基类的资源的能力 protected virtual void ReleaseUnmanageResources() { //Do something... } //私有函数用以释放非托管资源 private void Dispose(bool disposing) { ReleaseUnmanageResources(); //为true时表示是客户显式调用了释放函数,需通知GC不要再调用对象的Finalize方法 //为false时肯定是GC调用了对象的Finalize方法,所以没有必要再告诉GC你不要调用我的Finalize方法啦 if (disposing) { GC.SuppressFinalize(this); } } } static void Main(string[] args) { //tmpObj1没有手工释放资源,就等着GC来慢慢的释放它吧 Class1 tmpObj1 = new Class1(); //tmpObj2调用了Dispose方法,传说比等着GC来释放它效率要调一些 //个人认为是因为要逐个对象的查看其元数据,以确认是否实现了Dispose方法吧 //当然最重要的是我们可以自己确定释放的时间以节省内存,优化程序运行效率 Class1 tmpObj2 = new Class1(); ((IDisposable)tmpObj2).Dispose(); } } }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值