C#基础六

本文详细介绍了进程和线程的概念,包括主线程与子线程的关系,四种创建线程的方式,线程的生命周期管理,以及.NET中Task类和ThreadPool的使用,强调了异步编程、同步与await的重要性。
摘要由CSDN通过智能技术生成

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

进程 主线程和分线程关系

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

  有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"); 

线程阻塞 阻塞主线程 t.Join()           
Thread t = new Thread(() => { for (int i = 0; i < 1000; i++) Console.Write("y"); });
t.Start();
t.Join();//线程阻塞
Console.WriteLine("Thread t has ended!"); 

线程抢占   如果两个线程同时对某个资源进行同时访问  就可能出现 线程抢占         
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和Thread一样,位于System.Threading命名空间下

//2.Thread每次都会创建新线程,线程的创建和销毁是一个开销比较大的操作,Task每次执行将不会立即创建一个新线程,而是到CLR线程池查看是 否有空闲的线程,有的话就取一个线程处理这个请求,处理完请求后再把线程放回线程池,这个线程也不会立即撤销,而是设置为空闲状态,可供线程池再次调度, 从而减少开销。

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> task4 = new Task<int>(() => {
    return 1111;
});     
task4.Start();
task4.ContinueWith(tas =>
{
    Console.WriteLine("task finished!");
    //获取task4 完成后的返回值结果
    Console.WriteLine(tas.Result); 
});
       
 
例子2:一个Task完成后自动启动下一个Task,使用Task的延续
static void Main(string[] args)
{
    Task task = Task.Run(() => {    
    Thread.Sleep(3000);
    Console.WriteLine("1");
});

var result = task.ContinueWith<string>(tas =>
{
    Console.WriteLine("task finished!");
    Task task1 = Task.Run(() =>
    {
        Thread.Sleep(3000);
        Console.WriteLine("2");
    });
    return "This is task 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#会另起一个线程执行await后面的代码。

不使用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;
        }
 
 
 
 
 
async/await winform异步使用 :
private async void button1_Click(object sender, EventArgs e)
{
    var t = Task.Run(() =>
    {
        Thread.Sleep(5000);
        return "Hello";
    });
    string text = await t;
    this.Text = text;
}
单击按钮后,程序执行到 string text = await t  会直接返回到调用方法的地方继续执行,t方法异步执行返回后为text赋值并继续执行执行 this.Text = text(回调在主线程执行),效果就是点击按钮后界面不会卡死,方法执行完后 this.Text 更改。
 
寥寥几行就搞定了,不用再多写那么多函数,使用起来也很灵活。最让人头疼的跨线程修改控件的问题完美解决了,再也不用使用Invoke了,因为修改控件的操作压根就是在原来的线程上做的,还能不阻塞UI。

 //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;
}

  • 11
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值