c#基础6


进程与线程的一个简单解释 - 阮一峰的网络日志       进程和线程的理解

进程 主线程和分线程关系

一个程序就是一个进程,然而进程里面包含若干个线程,而每个进程里面都有一个(可以说必须要有一个)线程,这个线程就是主线程,然而主线程有一天发现自己的工作太多了,在规定的时间内完不成工作,这时候他就召唤了一个小弟(子线程)帮他,他给小弟分配了一些任务,当小弟做完了分配给他的任务后,他就把小弟赶走了!这就是主线程和子线程。

  有4种创建线程的方式:

  • 1.Thread 自己创建的独立的线程, 优先级高,需要使用者自己管理。
            //创建线程1

            //创建分线程执行哪个方法
            ThreadStart childref = new ThreadStart(CallToChildThread);
            //创建分线程实例对象
            Thread childThread = new Thread(childref);     
            //执行分线程
            childThread.Start();
             public static void CallToChildThread()
         {
           
           
              while (true)
            {

                Console.WriteLine("执行繁重的任务");
            }
          
            
          }



//  简写
            Thread childThread = new Thread(() => {
                while (true)
                {

                    Console.WriteLine("执行繁重的任务");
                }
            });
 

             //线程暂停2
             //创建分线程执行哪个方法
            ThreadStart childref = new ThreadStart(CallToChildThread);
            //创建分线程实例对象
            Thread childThread = new Thread(childref);
            // 设置线程的名字
            childThread.Name = "分线程1";
            //执行分线程
            childThread.Start();
        
             public static void CallToChildThread()
         {
           
             //线程休眠  当把方法写在哪个线程中就休眠哪个线程
             Thread.Sleep(3000);
           
                  while (true)
            {

                Console.WriteLine("执行繁重的任务");
            }
          
            
          }
    


//线程销毁
1.线程方法Method执行完结,线程自动销毁
2.如果是无限循环需要手动销毁


 

            //创建线程

            //创建分线程执行哪个方法
            ThreadStart childref = new ThreadStart(CallToChildThread);
            //创建分线程实例对象
            Thread childThread = new Thread(childref);
            childThread.Name = "分线程1";
            //执行分线程
            childThread.Start();
            //线程休眠  当把方法写在哪个线程中就休眠哪个线程
             Thread.Sleep(3000);

            //销毁线程
            childThread.Abort();
            Console.WriteLine("haha");


        }

        public static void CallToChildThread() {
         
            while (true)
            {

                Console.WriteLine("111111");
            }
          
            
        }


线程函数通过委托传递,可以不带参数,也可以带参数(只能有一个参数)
 Thread t2 = new Thread(new ParameterizedThreadStart(TestMethod));
 t2.Start("hello");

  public static void TestMethod(object data)
        {
            string datastr = data as string;
            Console.WriteLine("带参数的线程函数,参数为:{0}", datastr);
        }


  //简写
  Thread t2 = new Thread((e) => { Console.WriteLine("带参数的线程函数,参数为:{0}", e); });
  t2.Start("hello");



线程阻塞 Join()  线程之间执行顺序默认是无关的  为了保持同步  使用join()
           

            Thread thread1 = new Thread(() => 
            { 
                Console.WriteLine("2"); 
            });
            Thread thread2 = new Thread(() => { 
               //线程同步
                thread1.Join();
                 Console.WriteLine("3"); 
            
            });
            Thread thread3 = new Thread(() => { 
                thread2.Join();
                Console.WriteLine("4");
            });
            thread1.Start();
          
            thread2.Start();
          
            thread3.Start();



线程抢占   如果两个线程同时对某个资源进行同时访问  就可能出现 线程抢占 

        static  bool done;
        static void Main(string[] args)
        {
              
            new Thread(Go).Start();
            Go();
            Console.ReadKey();
        }

        static  void Go()
        {
            if (!done)
            {
                Console.WriteLine("Done");
                done = true;
            }
        }


解决线程抢占问题 使用线程锁
         static  readonly object locker = new object();  //线程锁对象
        static  bool done;
        static void Main(string[] args)
        {
              
            new Thread(Go).Start();
            
            Go();
            Console.ReadKey();
        }


        static  void Go()
        {
            //lock关键字 线程锁  (唯一的对象)
            lock (locker)
            {

                if (!done)
                {
                    Console.WriteLine("Done");
                    done = true;
                }
            }
           
        }


前台线程与后台线程  
前台线程会随着主线程窗口关闭而停止,后台线程及时主线程窗口关闭自己独立运行。
IsBackground 设置是否是前后台线程
    Thread worker = new Thread(() => Console.ReadLine());
    worker.IsBackground = true;  
    worker.Name = "backThread";
    worker.Start();
    Console.WriteLine("finish!");

  • 2.ThreadPool 线程池
  •  线程和线程池都是进行多线程操作的,线程池是用来保存线程的一个容器,在程序创建线程来执行任务的时候线程池才会初始化一个线程,线程在执行完毕之后并不会被销毁,而是被挂起等待下一个任务的到来被激活执行任务,当线程池里的线程不够用的时候会新实例化一个线程,来执行,线程池里的线程会被反复利用。

        


方式一:
QueueUserWorkItem 接收一个参数,参数类型是WaitCallback 它是一个无返回值委托,委托函数接收一个obejct类型参数。
官方定义:public delegate void WaitCallback(object state);



   WaitCallback callBack = DoSomething;
   ThreadPool.QueueUserWorkItem(callBack);
  
  static void DoSomething(object obj)
  {
     Console.WriteLine("do something");
  }


以上代码可以简写, waitCallback委托赋值一个匿名方法

WaitCallback waitCallback = arg => Console.WriteLine("dosomething1");
ThreadPool.QueueUserWorkItem(waitCallback)

继续简写
ThreadPool.QueueUserWorkItem(e =>
{
    Console.WriteLine("do something2");
});




方式二:
QueueUserWorkItem 接收两个参数,第一个参数是WaitCallback 委托类型,第二个参数是object对象,用于传递给委托函数

参数"dosomething3"将传递给委托函数DoSomething



static void DoSomething(object value)
{
    Console.WriteLine(value);
}

WaitCallback waitCallback1 = DoSomething;
ThreadPool.QueueUserWorkItem(waitCallback1, "dosomething3");


以上代码简写
使用匿名函数进行简写:

ThreadPool.QueueUserWorkItem(e =>
{
    DoSomething(e);
}, "dosomething4");





线程休眠
            
            //ManualResetEvent mreset = new ManualResetEvent(false);
          ThreadPool.QueueUserWorkItem(e =>
            {
                Thread.Sleep(2000);
                  Console.WriteLine("dosomething");
              //  mreset.Set();
            });
            Console.WriteLine("dosomething else...");
            Console.WriteLine("dosomething else...");
           //阻塞主线程 等待分线程完成后 执行mreset.Set()后执行后续代码
             mreset.WaitOne();
  • 3.net4.0在ThreadPool的基础上推出了Task类
  • ThreadPool相比Thread来说具备了很多优势,但是ThreadPool却又存在一些使用上的不方便◆ ThreadPool不支持线程控制,线程延续 ,线程取消
  • task 解决了 以上问题

//1.Task位于using System.Threading.Tasks;命名空间下

2.Task和ThreadPool一样 都是线程池操作

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace DemoAsync
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Task   With Thread  Start !");
            for (int i = 0; i <= 5; i++)
            {
                Thread t = new Thread(Dotaskfunction);
                t.Start();
            }
            Console.WriteLine("Task   With Thread End !");

            Console.WriteLine("Task   With Task   Start !");
            for (int i = 0; i <= 5; i++)
            {
                Task.Run(() => { Dotaskfunction(); });
            }
            Console.WriteLine("Task   With Task End !");
            Console.ReadLine();

        }

        public static void Dotaskfunction()
        {
            Console.WriteLine("ThreadID: {0}", Thread.CurrentThread.ManagedThreadId);

        }

    }
}

2. 创建Task 

//方式1
//第一种创建方式,直接实例化:必须手动去Start   可以绑定有参数的委托对象
   var task1 = new Task(() =>
    {
       //TODO you code
    });
   task1.Start();


//方式2
//第二种创建方式,工厂创建,直接执行  且绑定的都是无参无返回值的委托对象
   var task2 = Task.Factory.StartNew(() =>
    {
     
    });

或者是
Task.Run(() =>{

});

三、Task的任务控制:Task比threadPool优点就是任务控制,很好的控制task的执行顺序,让多个task有序的执行


Task.Wait	task1.Wait();就是等待任务执行(task1)完成,task1的状态变为Completed。
Task.WaitAll	待所有的任务都执行完成:
Task.WaitAny	等待任何一个任务完成就继续向下执行
Task.ContinueWith	第一个Task完成后自动启动下一个Task,实现Task的延续
CancellationTokenSource	通过cancellation的tokens来取消一个Task。

  //task1.Wait();就是等待任务执行(task)完成
            Task task = Task.Run(() => {    
                
                Thread.Sleep(3000);
                Console.WriteLine("1"); 
            
            });
          等待任务执行(task)完成  后执行后续代码
            task.Wait();
            Task task1 = Task.Run(() => { Console.WriteLine("2"); });
            Task task2 = Task.Run(() => { Console.WriteLine("3"); });
            
         

            Console.WriteLine("All task finished!");


//Task.WaitAll	待所有的任务都执行完成
 Task task = Task.Run(() => {    
                
                Thread.Sleep(3000);
                Console.WriteLine("1"); 
            
            });
            Task task1 = Task.Run(() => { Console.WriteLine("2"); });
            Task task2 = Task.Run(() => { Console.WriteLine("3"); });
            Task.WaitAll(task, task1, task2);
             待所有的任务都执行完成 执行以下内容
            Console.WriteLine("All task finished!");


Task.WaitAny	等待任何一个任务完成就继续向下执行

  Task task = Task.Run(() => {    
                
                Thread.Sleep(3000);
                Console.WriteLine("1"); 
            
            });
       
            Task task1 = Task.Run(() => { Console.WriteLine("2"); });
            Task task2 = Task.Run(() => { Console.WriteLine("3"); });
            //等待其中任意一个任务完成后 执行后续代码
            Task.WaitAny(task, task1, task2);

            Console.WriteLine("All task finished!");



       Task.ContinueWith  线程延续  :如果线程中的结果 需要再后续使用 使用线程延续

           //线程延续1

            Task<int> task10 = Task<int>.Run(() =>
            {
                for (int i = 0; i < 5; i++)
                {

                    Thread.Sleep(1000);
                    Console.WriteLine(i);
                }
                return 30;
            });

            task10.ContinueWith(tas =>
               {
                   //tas 代表 Task<int> task10
                   //获取task10的返回值结果
                   Console.WriteLine("task10任务执行完毕");
                   Console.WriteLine(tas.Result);

               });

            Console.WriteLine("11");


          //  线程延续2
            Task task11 = Task.Run(() =>
            {

                Console.WriteLine("1");

            });

            var result = task11.ContinueWith<string>(tas =>
            {

                Console.WriteLine("2");
                Task task1 = Task.Run(() =>
                {

                    Console.WriteLine("3");
                });
                Console.WriteLine("4");
                return "This is task result!";
            });


            Console.WriteLine(result.Result);
            延续3
            Task<float> task12 = Task.Run(() =>
            {
                return 10.0f;
            });
            var task13 = task12.ContinueWith<int>(tas =>
             {

                 10.0f;
                 Console.WriteLine(tas.Result);
                 return (int)(tas.Result + 10);
             });

            Console.WriteLine(task13.Result);
 task线程取消 方法

static void Main(string[] args)
        {
           
            //1.初始化线程取消类
            var tokenSource = new CancellationTokenSource();
            //2.获取线程取消标记
            var token = tokenSource.Token;
            //3.开启task线程  并且绑定取消线程标记
            var task = Task.Run(() =>
            {
                for (var i = 0; i < 1000; i++)
                {
                    Thread.Sleep(1000);
                    //是否执行取消方法 如果取消  为true  反之为 false
                    if (token.IsCancellationRequested)
                    {
                        Console.WriteLine("Abort mission success!");
                        return;
                    }
                }
            }, token);
            //取消线程后回调方法
            token.Register(() =>
            {
                Console.WriteLine("Canceled");
            });
            Console.WriteLine("Press enter to cancel task...");
            Console.ReadKey();
            //取消线程方法
            tokenSource.Cancel();
            Console.ReadKey();
        }

异步侧重于任务的执行顺序,而多线程则是关于多个线程如何并发执行。应该说多线程是实现异步的常用手段,但不能说他们是一回事。即便是只有一个线程的情况下,我们仍然可以实现异步

异步和同步

异步:表示执行某项操作之后不等待操作结束,但可以在操作结束后收到通知

同步反之

1. net5.0推出了async/await    async/await特性是与Task紧密相关的

2.async 是“异步”的简写,sync 是“同步”的简写

 await 是 async wait 的简写。await 用于等待一个异步方法执行完成

           //如何通过使用async/await  完成异步编程
            //1. async 必须修饰方法  被修饰的方法 表示是一个异步方法

            //2.async 和await必须连用  如果不使用await 那么这个方法还是同步方法
            //3.async 描述的方法 的返回值类型必须是void 或者是task 或者task<T>
            //4.await 描述的也是方法 但是必须是使用线程(task)的方法
            //5.Async方法在执行的时候,开始是以同步的方式执行,直到遇到await关键字,
            //从await关键字开始,C#会另起一个线程执行 async方法之外的其他代码

不使用async/await 完成下载逻辑,则代码执行顺序时混乱的,不符合业务逻辑:

internal class Program
    {
        static void Main(string[] args)
        {
            DownloadHandle();
            Console.ReadLine();
        }
        /// <summary>
        /// 1. 通知用户下载开始
        /// 2. 提示下载完成
        /// 3. 开始下载
        /// </summary>
        public static void DownloadHandle()
        {
            Console.WriteLine("下载开始!->主线程ID:" + Thread.CurrentThread.ManagedThreadId);
            Download();
            Console.WriteLine("下载完成!->主线程ID:" + Thread.CurrentThread.ManagedThreadId);

        }
        /// <summary>
        /// 下载
        /// </summary>
        /// <returns></returns>
        public static Task Download()
        {
            return Task.Run(() =>
            {
                Console.WriteLine("下载线程ID:->" + Thread.CurrentThread.ManagedThreadId);
                Console.WriteLine("10%");
                Console.WriteLine("30%");
                Console.WriteLine("50%");
                Console.WriteLine("60%");
                Console.WriteLine("80%");
                Console.WriteLine("99%");
                Console.WriteLine("100%");
            });
        }
    }
使用async/await 完成下载逻辑,则代码执行顺序正常,符合业务逻辑:




internal class Program
    {
        static void Main(string[] args)
        {
            DownloadHandle();
            Console.ReadLine();
        }
        /// <summary>
        /// 模拟下载
        /// </summary>
        public static async void DownloadHandle()
        {
            Console.WriteLine("下载开始!->主线程ID:" + Thread.CurrentThread.ManagedThreadId);
            await Download();
            Console.WriteLine("下载完成!->主线程ID:" + Thread.CurrentThread.ManagedThreadId);

        }
        /// <summary>
        /// 下载
        /// </summary>
        /// <returns></returns>
        public static Task Download()
        {
            Console.WriteLine("下载线程ID:->" + Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("10%");
            Console.WriteLine("30%");
            Console.WriteLine("50%");
            Console.WriteLine("60%");
            Console.WriteLine("80%");
            Console.WriteLine("99%");
            Console.WriteLine("100%");
            return Task.CompletedTask;
        }


winform使用BeginInvoke 和await 完成数据加载(后续讲)

 //BeginInvoke

 private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            //接收缓冲区数据的字节数
            int size = serialPort1.BytesToRead;
            //动态创建数组接收数据
            byte[] buffer = new byte[size];
            //读取数据
            serialPort1.Read(buffer, 0, buffer.Length);

            string msg = Encoding.Default.GetString(buffer);

            //异步执行,
            //txtReceive.BeginInvoke(msgDelegate, msg);
            txtReceive.BeginInvoke(new Action<string>(str =>
            {
                txtReceive.Text = str;
            }), msg);
        }






 private async void serialPort1_DataReceived1(object sender, SerialDataReceivedEventArgs e)
        {
           
            //await异步执行
            var t = Task.Run(() =>
            {
                //接收缓冲区数据的字节数
                int size = serialPort1.BytesToRead;
                //动态创建数组接收数据
                byte[] buffer = new byte[size];
                //读取数据
                serialPort1.Read(buffer, 0, buffer.Length);

                string msg = Encoding.Default.GetString(buffer);
                return msg;
            });
            string str = await t;
            txtReceive.Text = str;
        }

  

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值