回调函数 线程_Siki——C#高级教程 (44~52) 线程、任务和同步

ae9da9dac4fc64fc53cb16f428927b43.png

主要内容

  • 进程和线程的概念
  • 线程开启方式-异步委托
  • 检测委托线程的结束(通过等待句柄和回调函数)
  • 线程开启方式2-通过Thread类
  • 线程的其它概念后台和前台线程,线程的优先级,线程的状态
  • 线程开启方式3-线程池
  • 线程开启方式4-任务
  • 任务的其他知识
  • 线程问题-争用条件和死锁

进程和线程的概念

45b35b782a8bfc5e4e25c08234151fdb.png
图示 线程的解释
进程和线程的一个简单解释(资料来源-阮一峰)
  1. 计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行。
  2. 如果工厂的电力有限一次只能供给一个车间使用。也就是说一个车间开工的时候,其他车间就必须停工。背后的含义就是单个CPU一次只能运行一个任务(多核CPU可以运行多个任务)。
  3. 进程就好比工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。
  4. 一个车间里可以有很多工人,他们协同完成一个任务。
  5. 线程就好比车间里的工人,一个进程可以包括多个线程。
  6. 车间的控件是工人们共享的,比如许多房间是每个工人都可以进出的。这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享空间。
  7. 进程就好比工厂的车间,它代表CPU所能处理的单个任务。任意时刻,CPU总是运行一个进程,其他进程处于非运行状态。
  8. 一个防止他人进入的简单方法,就是门口加一把锁(厕所)。先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去。这就叫"互斥锁"(Mutual exclusion,缩写为Mutex)防止多个线程同时读写某一块内存区域。
  9. 还有些房间,可以同时容纳n个人,比如厨房。也就是说,如果人数大于n,多出来的人只能在外面等着。这好比某些内存区域,只能供给固定数目的线程使用。
  10. 这时的解决方法,就是在门口挂n把钥匙。进去的人就取一把钥匙,出来时再把钥匙挂回原处。后到的人发现钥匙架空了,就知道必须在门口排队等着了。这种做法叫做"信号量"(Semaphore),用来保证多个线程不会互相冲突。

不难看出,Mutex是Semaphore的一种特殊情况(n=1时)。也就是说,完全可以用后者替代前者。但是,因为Mutex较为简单且效率高,所以在必须保证资源独占的情况下,可以采用这种设计。

操作系统的设计,因此可以归结为三点:

  • 以多进程形式,允许多个任务同时运行;
  • 以多线程形式,允许单个任务分成不同的部分运行;
  • 提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源。

线程开启方式-异步委托

dd951f321be81e0a022583454691f441.png
图示 无参无返回值线程

91a0ac9f556e7d63716b88319839bbca.png
图示 有参无返回值线程
有参有返回值线程
using System;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        //一般我们会为比较耗时的操作,开启单独线程去执行
        static int Test(int i, string str)
        {
            Console.WriteLine("Test" + i + str);
            //让当前线程休眠(暂停线程的执行),ms
            Thread.Sleep(100);
            return 100;
        }
        //在Main线程中,语句的执行是从上到下的
        static void Main(string[] args)
        {
            //通过委托开启一个线程
            Func<int, string, int> a = Test;
            //开启一个新线程去执行a所引用的方法
            //IAsyncResult表示异步执行的状态
            //可以取得当前线程的状态
            IAsyncResult ar = a.BeginInvoke(66, "AAA", null, null);
            //可以认为线程是同时执行的(异步执行)
            Console.WriteLine("Main");
            //如果当前线程没有执行完成
            while (ar.IsCompleted == false)
            {
                Console.Write(".");
                Thread.Sleep(10);
            }
            int res = a.EndInvoke(ar);
            Console.WriteLine(res);
            Console.ReadKey();
        }
    }
}

3e4c2c2d41ffa8edba87739dc492e12b.png
图示 运行结果

检测委托线程的结束(通过等待句柄和回调函数)

等待句柄
using System;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        //一般我们会为比较耗时的操作,开启单独线程去执行
        static int Test(int i, string str)
        {
            Console.WriteLine("Test" + i + str);
            //让当前线程休眠(暂停线程的执行),ms
            Thread.Sleep(100);
            return 100;8
        }
        //在Main线程中,语句的执行是从上到下的
        static void Main(string[] args)
        {
            //通过委托开启一个线程
            Func<int, string, int> a = Test;
            //开启一个新线程去执行a所引用的方法
            //IAsyncResult表示异步执行的状态
            //可以取得当前线程的状态
            IAsyncResult ar = a.BeginInvoke(66, "AAA", null, null);
            //可以认为线程是同时执行的(异步执行)
            Console.WriteLine("Main");
            //检测线程结束
            //AsyncWaitHandle获取用于等待异步操作的完成
            //WaitOne阻止当前线程,直到当前的System.Threading.WaitHandle 收到信号为止,
            //同时使用 32 位带符号整数指定时间间隔
            //并指定是否在等待之前退出同步域。
            bool isEnd = ar.AsyncWaitHandle.WaitOne(1000);
            if (isEnd)
            {
                int res = a.EndInvoke(ar);
                Console.WriteLine(res);
            }
            Console.ReadKey();
        }
    }
}

9c74f32357a1fd413f44388ad71ffb73.png
图示 运行效果
回调函数
using System;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        //一般我们会为比较耗时的操作,开启单独线程去执行
        static int Test(int i, string str)
        {
            Console.WriteLine("Test" + i + str);
            //让当前线程休眠(暂停线程的执行),ms
            Thread.Sleep(100);
            return 100;
        }
        //在Main线程中,语句的执行是从上到下的
        static void Main(string[] args)
        {
            //通过回调 检测线程结束
            Func<int, string, int> a = Test;
            //倒数第二个参数是一个委托类型的参数,表示回调函数,就是当线程结束的时候会调用这个委托指向的方法
            //倒数第一个参数用来给回调函数传递数据
            IAsyncResult ar = a.BeginInvoke(66, "AAAA", OnCallBack, a);
            Console.ReadKey();
        }
        static void OnCallBack(IAsyncResult ar)
        {
            Func<int, string, int> a = ar.AsyncState as Func<int, string, int>;
            int res = a.EndInvoke(ar);
            Console.WriteLine(res + "在回调函数中取得结果");
        }
    }
}

a22316a284c6a1ecbf823e6d316cc112.png
图示 运行结果
using System;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        //一般我们会为比较耗时的操作,开启单独线程去执行
        static int Test(int i, string str)
        {
            Console.WriteLine("Test" + i + str);
            //让当前线程休眠(暂停线程的执行),ms
            Thread.Sleep(100);
            return 100;
        }
        //在Main线程中,语句的执行是从上到下的
        static void Main(string[] args)
        {
            //通过回调 检测线程结束
            Func<int, string, int> a = Test;
            a.BeginInvoke(100, "siki", ar =>
            {
                int res = a.EndInvoke(ar);
                Console.WriteLine(res + "在lambda表达式中取得");
            }, null);
            Console.ReadKey();
        }
    }
}

243bc089ace607c1bdd1326eec59c3ee.png
图示 运行效果

线程开启方式2-通过Thread类

6235c319fb3d8d0cb5d8f52c99585f64.png
图示 Thread类应用

ce906b9affb2a44e00dda89e1224951e.png
图示 Thread类-Lambda表达式
using System;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        //给Thread传递方法时,必须是object类型的
        static void DownLoadFile(object fileName)
        {
            //可通过线程ID进行管理
            Console.WriteLine("开始下载:" + Thread.CurrentThread.ManagedThreadId+ fileName);

            Thread.Sleep(2000);
            Console.WriteLine("下载完成");
        }

        static void Main(string[] args)
        {
            /*
             * 第一种方式(无参)
            //创建Thread对象,这个线程并没有启动
            Thread t = new Thread(DownLoadFile);
            t.Start();//开始去执行线程
            Console.WriteLine("Main");
            Console.ReadKey();
            */

            /*
             * 第二种方式(无参)
            Thread t = new Thread(() =>
            {
                Console.WriteLine("开始下载:" + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(2000);
                Console.WriteLine("下载完成");
            });
            t.Start();
            Console.ReadKey();
        }  */
            Thread t = new Thread(DownLoadFile);
            t.Start("xxxx");//开始去执行线程
            Console.ReadKey();
        }
    }
}

30d3cb01129e5fdd36f3403c1709f3ec.png
图示 运行结果

3349a652c3e5245c3664773798a526ca.png
图示 给线程传递数据
创建MyThread类(定义传递的数据)
using System;
using System.Threading;

namespace ConsoleApp1
{
    class MyThread
    {
        private string filename;
        private string filepath;
        public MyThread(string fileName, string filePath)
        {
            filename = fileName;
            filepath = filePath;
        }
        //提供一个线程方法,用于下载
        public void DownFile()
        {
            Console.WriteLine("开始下载" + filename + filepath);
            Thread.Sleep(2000);
            Console.WriteLine("下载完成") ;
        }
    }
}
开启线程
using System;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            MyThread my = new MyThread("xxx.bt  ","http://www.xxx.com");
            //构造Thread时候,可以传递一个静态方法,也可以传递一个动态普通方法
            Thread t= new Thread(my.DownFile);
            t.Start();
            Console.ReadKey();
        }
    }
}

12d561554e94358a570ccd08be1d5c28.png
图示 运行效果

线程的其它概念后台和前台线程,线程的优先级,线程的状态

后台和前台线程

ec2850c612e61b9aec2dea05ed6ae8f8.png
图示 后台和前台线程介绍
using System;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        //给Thread传递方法时,必须是object类型的
        static void DownLoadFile(object fileName)
        {
            //可通过线程ID进行管理
            Console.WriteLine("开始下载:" + Thread.CurrentThread.ManagedThreadId + fileName);
            Thread.Sleep(2000);
            Console.WriteLine("下载完成");
        }
        static void Main(string[] args)
        {   //前台线程 
            //Thread t= new Thread(DownLoadFile);
            //后台线程
            Thread t = new Thread(DownLoadFile);
            t.IsBackground = true;//设置为后台线程
            t.Start("xxx");
        }
    }
}
线程的优先级

f1fc0137b31f4a2b6acc792ed814c834.png
图示 线程的优先级

b886bd9375347ebdc72f8b814b6aa898.png
图示 控制线程的方法

线程开启方式3-线程池

1e0f02565e444d0c9f10cd2025a45c2d.png
图示 线程池的介绍

38662968cfbe4fddc8278854cf547fba.png
图示 线程池示例
using System;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        static void ThreadMethod(object state)
        {
            Console.WriteLine("线程开始:" + Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(2000);
            Console.WriteLine("线程结束:" + Thread.CurrentThread.ManagedThreadId);
        }
        static void Main(string[] args)
        {
            ThreadPool.QueueUserWorkItem(ThreadMethod);//开启一个工作线程
            ThreadPool.QueueUserWorkItem(ThreadMethod);
            ThreadPool.QueueUserWorkItem(ThreadMethod);
            ThreadPool.QueueUserWorkItem(ThreadMethod);
            ThreadPool.QueueUserWorkItem(ThreadMethod);
            ThreadPool.QueueUserWorkItem(ThreadMethod);
            ThreadPool.QueueUserWorkItem(ThreadMethod);
            Console.ReadKey();
        }
    }
}

8e1bfcbf1cec1cd057f6f5d75816e652.png
图示 运行效果

线程开启方式4-任务

2345151ddcd86e78083505ad3ce3b231.png
图示 线程开启方式-任务
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void ThreadMethod()
        {
            Console.WriteLine("任务开始");
            Thread.Sleep(2000);
            Console.WriteLine("任务结束");
        }
        static void Main(string[] args)
        {

            //传递一个需要线程去执行的方法
            //Task t = new Task(ThreadMethod);
            //t.Start();

            TaskFactory tf = new TaskFactory();
            Task t = tf.StartNew(ThreadMethod);

            Console.WriteLine("Main");
            Console.ReadKey();
        }
    }
}

267e6cca7cd33e1f18ebf4170a57cc8d.png
图示 运行效果

任务的其他知识

d41033188d296d917de398f1b2a4029d.png
图示 连续任务

73d839eadfd2cbe0b3e67c5d79ba1330.png
图示 任务层次结构

线程问题-争用条件和死锁

a8a41e60f7f11973ec838f04d74c68e6.png
图示 使用lock(锁)解决争用条件的问题

总结

学习siki的C#教程,打基础。

该套视频在泰课在线搜索《第三季 C#编程高级篇》,可以在线学习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值