线程理论

什么是进程?

当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源。

而一个进程又是由多个线程所组成的。

 

什么是线程?

线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数。

 

什么是多线程?

多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。

 

多线程的好处: 

可以提高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;

});

 

posted @ 2020-04-25 15:30  安以痕_陈  阅读(114)  评论(0编辑  收藏  举报