线程理论
什么是进程?
当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源。
而一个进程又是由多个线程所组成的。
什么是线程?
线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数。
什么是多线程?
多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
多线程的好处:
可以提高CPU的利用率。在多线程程序中,一个线程必须等待的时候,CPU可以运行其它的线程而不是等待,这样就大大提高了程序的效率。
多线程的不利方面:
线程也是程序,所以线程需要占用内存,线程越多占用内存也越多;
多线程需要协调和管理,所以需要CPU时间跟踪线程;
线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题;
线程太多会导致控制太复杂,最终可能造成很多Bug;
Thread
static void Main(string[] args)
{
#region Thread无参数举例
Thread th = new Thread(ThreadChild);
th.Start();
Console.WriteLine("Main Thread Start!");
#endregion
}
static void ThreadChild()
{
Console.WriteLine("Child Thread Start!");
}
//带参数
static void Main(string[] args)
{
#region 使用parameterizedThreadStart委托执行带参数的委托
Thread th2 = new Thread(Thread_param);
th2.Start(20);
#endregion
}
static void Thread_param(object msg)
{
int message = (int)msg;
Console.WriteLine("Result:{0}",message);
}
上面创建的线程是类型不安全的,那用什么样的方式执行带传入参数的线程的方法是类型安全的呢,答案就是创建一个自定义类,在类中定义一个作为传入参数的字段,将线程的主方法定义为一个类的实例方法。然而使用这种方法就可以使用泛型来解决使用ParameterizedThreadStart的类型不安全
看招!!!!
class Program
{
static void Main(string[] args)
{
#region 使用自定义类实现带参数的线程
MyThread<string> mythread = new MyThread<string>("Thread_child");
Thread th3 = new Thread(mythread.ThreadChild);
th3.Start();
#endregion
}
}
class MyThread<T>
{
private T data;
public MyThread(T data)
{
this.data = data;
}
public void ThreadChild()
{
Console.WriteLine("Child Thread Start! Result:{0}",data);
}
}
------------------------
string name = string.Format("123");
ThreadStart method = () => this.TestThread(name);
Thread thread = new Thread(method);//1 默认前台线程:程序退出后,计算任务会继续
thread.IsBackground = true;//2 后台线程:程序退出,计算立即结束
thread.Start();
threadList.Add(thread);
ParameterizedThreadStart method = o => this.TestThread(o.ToString());
//Thread thread = new Thread(method);
//thread.Start(name);
static void PrintNumbers()
{
Console.WriteLine("Starting...");
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i);
}
}
//注意:要使用ParameterizedThreadStart,定义的参数必须为object
static void PrintNumbers(object count)
{
Console.WriteLine("Starting...");
for (int i = 0; i < Convert.ToInt32(count); i++)
{
Console.WriteLine(i);
}
}
Thread t1 = new Thread(new ThreadStart(PrintNumbers));//无参数的委托 t1.Start(); Thread t2 = new Thread(new ParameterizedThreadStart(PrintNumbers));//有参数的委托 t2.Start(10);
多个线程等待
List<Thread> threadList = new List<Thread>();
for (int i = 0; i < 5; i++)
{
string name = string.Format("btnThread_Click_{0}", i);
ThreadStart method = () => this.TestThread(name);
Thread thread = new Thread(method);//1 默认前台线程:程序退出后,计算任务会继续
thread.IsBackground = true;//2 后台线程:程序退出,计算立即结束
thread.Start();
threadList.Add(thread);
}
foreach (Thread thread in threadList)
{
thread.Join();将线程插入主线程,若把这句放到上面相当于单线程啦。
}
/// <summary>
/// 使用Thread 完成多线程回调
/// </summary>
/// <param name="method">要多线程执行的任务</param>
/// <param name="callback">回调执行的任务</param>
private void ThreadBeginInvoke(ThreadStart method, Action callback)
{
ThreadStart methodAll = new ThreadStart(() =>
{
method.Invoke();
callback.Invoke();
});
Thread thread = new Thread(methodAll);
thread.Start();
}
ThreadPool
线程池,用资源换时间 都是后台线程
上面介绍了介绍了平时用到的大多数的多线程的例子,但在实际开发中使用的线程往往是大量的和更为复杂的,这时,每次都创建线程、启动线程。从性能上来讲,这样做并不理想(因为每使用一个线程就要创建一个,需要占用系统开销);从操作上来讲,每次都要启动,比较麻烦。为此引入的线程池的概念。 好处: 1.减少在创建和销毁线程上所花的时间以及系统资源的开销 2.如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存以及”过度切换”。 在什么情况下使用线程池?
1.单个任务处理的时间比较短 2.需要处理的任务的数量大 线程池最多管理线程数量=“处理器数 * 250”。也就是说,如果您的机器为2个2核CPU,那么CLR线程池的容量默认上限便是1000 通过线程池创建的线程默认为后台线程,优先级默认为Normal。 |
//ThreadPool.SetMaxThreads(8, 8);//最小也是核数
//ThreadPool.SetMinThreads(8, 8);
static void Main(string[] args) { ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadMethod1), new object()); //参数可选 Console.ReadKey(); }
public static void ThreadMethod1(object val) { for (int i = 0; i <= 500000000; i++) { if (i % 1000000 == 0) { Console.Write(Thread.CurrentThread.Name); } } } |
string name = string.Format("btnThreadPool_Click_{0}", i);
WaitCallback method = t => this.TestThread(t.ToString());
ThreadPool.QueueUserWorkItem(method, name);
线程池等待
ManualResetEvent mre = new ManualResetEvent(false);
new Action(() =>//false 则关闭,该线程一直走不下去
{//这里异步打开
Thread.Sleep(5000);
Console.WriteLine("委托的异步调用");
mre.Set();//打开
}).BeginInvoke(null, null);
mre.WaitOne();
Console.WriteLine("12345");
mre.Reset();//关闭
---------------------------------------------------------------------
ManualResetEvent mre = new ManualResetEvent(false);
WaitCallback method = t =>
{
this.TestThread(t.ToString());
mre.Set();
};
ThreadPool.QueueUserWorkItem(method, "TestManualResetEvent");
Console.WriteLine("我们来干点别的。。。。");
Console.WriteLine("我们来干点别的。。。。");
Console.WriteLine("我们来干点别的。。。。");
Console.WriteLine("我们来干点别的。。。。");
mre.WaitOne();
Task 都是后台线程 基于线程池的
TaskFactory taskFactory = new TaskFactory();
for (int i = 0; i < 5; i++)
{
string name = string.Format("btnAsync_Click_{0}", i);
Action act = () => this.TestThread(name);
//taskFactory.StartNew(act);
//Task task = new Task(act);
//task.Start();
Task task = Task.Run(act);
}
线程等待
List<Task> taskList = new List<Task>();
Task any = taskFactory.ContinueWhenAny(taskList.ToArray(), t =>
{
//t.AsyncState
Console.WriteLine("这里是ContinueWhenAny {0}", Thread.CurrentThread.ManagedThreadId);
});
Task all = taskFactory.ContinueWhenAll(taskList.ToArray(), tList =>
{
Console.WriteLine("这里是ContinueWhenAll {0}", Thread.CurrentThread.ManagedThreadId);
});
不卡线程
Task.WaitAny(taskList.ToArray());//执行的线程等待某一个task的完成
Console.WriteLine("after WaitAny{0}", Thread.CurrentThread.ManagedThreadId);
Task.WaitAll(taskList.ToArray());//执行的线程等待全部的task的完成
Console.WriteLine("after WaitAll{0}", Thread.CurrentThread.ManagedThreadId);
卡线程,不一定是主线程
Parallel 基于task
主线程参与计算
//Parallel.Invoke(() => this.TestThread("btnParallel_Click_0")
// , () => this.TestThread("btnParallel_Click_1")
// , () => this.TestThread("btnParallel_Click_2")
// , () => this.TestThread("btnParallel_Click_3")
// , () => this.TestThread("btnParallel_Click_4"));
//等于使用4个task,然后主线程同步invoke一个委托 然后主线程waitall
//Parallel.For(6, 10, t =>
//{
// string name = string.Format("For btnParallel_Click_{0}", t);
// this.TestThread(name);
//});
//Parallel.ForEach(new int[] { 5, 6, 7, 10, 8473847 }, t =>
//{
// string name = string.Format("ForEach btnParallel_Click_{0}", t);
// this.TestThread(name);
//});
ParallelOptions parallelOptions = new ParallelOptions()
{
MaxDegreeOfParallelism = 5//控制线程个数
};
Parallel.For(6, 15, parallelOptions, (t, state) =>
{
string name = string.Format("btnParallel_Click_{0}", t);
this.TestThread(name);
//state.Break();//退出单次循环
//state.Stop();//退出全部的循环
//return;
});