• 十二、异步_实践


     一、线程实践步骤

    建立数据表 提供两个版本Mysql和sqlserver 两种

    create database Shop 
    go
    use Shop
    go
    
    create table Phone
    (
    	Id int primary key identity(1,1),
    	Phone varchar(32),
    	PCount int
    )
    
    
    insert into Phone values('vivo nex',3)
    
    select * from Phone
    
    
    create table User_X_Phone
    (
    	Id int primary key identity(1,1),
    	PhoneId int,
    	UserId varchar(64)
    )
    
    
    select * from Phone
    select * from User_X_Phone
    
    truncate table Phone
    truncate table User_X_Phone
    insert into Phone values('vivo nex',3)
    

      SqlServer版本的

    create database Shop 
    go
    use Shop
    go
    
    CREATE TABLE Phone (
    	Id int PRIMARY KEY AUTO_INCREMENT,
    	Phone varchar(32),
    	PCount int
    )
    
    INSERT INTO `Phone` (`Id`, `Phone`, `PCount`) VALUES (NULL, 'vivo nex', '3');
    
    select * from Phone
    
    
    create table User_X_Phone
    (
    	Id int PRIMARY KEY AUTO_INCREMENT,
    	PhoneId int,
    	UserId varchar(64)
    )
    
    
    select * from Phone
    select * from User_X_Phone
    
    truncate table Phone
    truncate table User_X_Phone
    insert into Phone values('vivo nex',3)
    

      MySql版本

    二、项目创建 我们使用FrameWork构建的控制台项目操作。

    添加链接字符串

      <connectionStrings>
        <add name="connString" connectionString="server=47.94.174.85;database=testDb;uid=testDb;password=testDb" providerName="System.Data.SqlClient" />
      </connectionStrings>
    

      链接代码操作

    using System.Configuration;
    using System.Data;
    using System.Data.SqlClient;
    
    namespace ConsoleApp1
    {
        class Program
        {
           
            private static readonly string connectionString  = ConfigurationManager.ConnectionStrings["connString"].ConnectionString;
            static void Main(string[] args)
            {
                using (SqlConnection connection = new SqlConnection(connectionString))
                {
                    if (connection.State == System.Data.ConnectionState.Closed)
                    {
                        connection.Open();//执行连接
                    }
                    else
                    {
                        SqlDataAdapter sqlDa = new SqlDataAdapter("SELECT * FROM `Phone`", connection);
                        DataTable dt = new DataTable();
                        sqlDa.Fill(dt);
                    }
                }
            }
        }
    }
    

      

    下面是Mysql通过字符串链接操作

     

    写代码

    using MySql.Data.MySqlClient;
    using System;
    using System.Configuration;
    using System.Data;
    using System.Data.Common;
    using System.Data.SqlClient;
    
    namespace ConsoleApp1
    {
        class Program
        {
    
            private static readonly string connectionString = ConfigurationManager.ConnectionStrings["connString"].ConnectionString;
            static void Main(string[] args)
            {
                try
                {
                    using (MySqlConnection connection = new MySqlConnection(connectionString))      //MySqlClient 手动添加引用 using MySql.Data.MySqlClient;
                    {
                        if (connection.State == System.Data.ConnectionState.Closed)
                        {
                            connection.Open();//执行连接
                        }
                        string sqlString = "SELECT * FROM `Phone`";
                        MySqlCommand cmd = new MySqlCommand(sqlString, connection);
                        MySqlDataReader reader = cmd.ExecuteReader();//执行ExecuteReader()返回一个MySqlDataReader对象
    
                        while (reader.Read())
                        {
                            if (reader.HasRows)//有一行读一行
                            {
                                Console.Write((string)reader["Phone"]);
                            }
                        }
                        Console.ReadKey();
                    }
    
                }
                catch (MySqlException ex)
                {
    
                    switch (ex.Number)
                    {
                        case 0:
                            Console.Write("Cannot connect to server.  Contact administrator");
                            break;
    
                        case 1045:
                            Console.Write("Invalid username/password, please try again");
                            break;
                    }
                }
            }
        }
    }
    

      

    直接EF操作方式实践展示

    实现基础服务

            static void Main(string[] args)
            {
                Model1 se = new Model1();
                //查询手机库存
                Phone phone = se.Phones.Where(a => a.Id == 1).FirstOrDefault();//表为复数
                if (phone.PCount < 1)
                {
                    Console.WriteLine("秒杀已经结束");
                }
                else
                {
                    //减去库存
                    phone.PCount--;
                    //添加一个抢成功的人
                    User_X_Phone uxp = new User_X_Phone() { PhoneId = 1 };
                    uxp.UserId = Guid.NewGuid().ToString();
                    se.User_X_Phone.Add(uxp);
                    se.SaveChanges();
                    Console.WriteLine("恭喜抢购成功");
                }
                //程序实现基础的买购(买卖购物)服务
            }
    

      

    开辟多线程方式-具体查看线程id

            static void Main(string[] args)
            {
                //方式一、开辟多线程方法
                //for (int i = 0; i <= 20; i++)
                //{
                //    Thread thread = new Thread(()=> {
                //        Console.WriteLine("线程运行");
                //    });
                //    thread.Start();
                //}
                //Console.WriteLine("主线程运行");
                //Console.ReadLine();
                //方式二、线程池方式开辟多线程
                //for (int i = 0; i < 10; i++)
                //{
                //    ThreadPool.QueueUserWorkItem(
                //        (a) =>
                //        {
                //            Console.WriteLine("线程运行" + a);
                //            Thread.Sleep(2000);//让线程延迟2秒执行一个
                //        }
                //     ,i);
                //}
                //Console.WriteLine("主线程运行");
                //Console.ReadLine();
    
                //方式三 Tasky异步方法,里面也是实现的多线程            
                for (int i = 0; i < 10; i++)
                {
                    Task task = new Task((a) =>
                    {
                        Console.WriteLine("线程运行" + a);
                    }, i);
                    task.Start();
                }
                Console.WriteLine("主线程运行");
                Console.ReadLine();
    
                /*
                //基础买购方法       
                Model1 se = new Model1();
                //查询手机库存
                Phone phone = se.Phones.Where(a => a.Id == 1).FirstOrDefault();//表为复数
                if (phone.PCount < 1)
                {
                    Console.WriteLine("秒杀已经结束");
                }
                else
                {
                    //减去库存
                    phone.PCount--;
                    //添加一个抢成功的人
                    User_X_Phone uxp = new User_X_Phone() { PhoneId = 1 };
                    uxp.UserId = Guid.NewGuid().ToString();
                    se.User_X_Phone.Add(uxp);
                    se.SaveChanges();
                    Console.WriteLine("恭喜抢购成功");
                }
                //程序实现基础的买购(买卖购物)服务*/
            }
    

      

    线程运行服务(出现的问题)

    --多线程
    --第一次情况我只有三个手机 循环抢购成功竟然有10个。
    --第二次竟然有抢购成功8个人。
    --第三次 抢购 手机还剩下一个 循环次数10人,成功10个 手机竟然还剩余1。

    普通进程运行服务

     

    解决线程出现的问题(异步加锁->异步队列==同步)

            static object varlock = new object();//一、静态变量
            static void Main(string[] args)
            {
                //实践
                for (int i = 0; i < 20; i++)
                {
                    Task task = new Task((obj) =>//标识线程id
                    {
                        Model1 se = new Model1();//在写代码时特别注意:同一个上下文实例,不在多个线程中使用
                        lock (varlock)   //二、加锁 仅是让异步方法内操作的时候去排队,仅仅与当前函数并行,从而不影响下行语句的执行,相当于异步处理
                        {
                            Phone phone = se.Phones.Where(a => a.Id == 1).FirstOrDefault();//表为复数 
                            //三、取数据有数量 有快,有慢-点在这个位置,主要语句-因异步等同于类似并行方式 线程异步问题主要出现在这个位置 每次查询后执行放在锁内
                            //比如6人还没到判断并且检查库存的时候,就读取到库存有3个,  然后判断到else去执行 剩下四个忙了 才到if里面
                            // 也可能 6人中有 2个人读取到有手机数是3个 另外两个读取到的时候手机是2个 最后两个人读取到的时候是1 (比较读取的时候,正时没有减去)
                            if (phone.PCount < 1)    
                            {
                                Console.WriteLine("秒杀已经结束");
                            }
                            else
                            {
                                //减去库存
                                phone.PCount--;
                                //添加一个抢成功的人
                                User_X_Phone uxp = new User_X_Phone() { PhoneId = 1 };
                                uxp.UserId = Guid.NewGuid().ToString();
                                se.User_X_Phone.Add(uxp);
                                se.SaveChanges();
                                Console.WriteLine("恭喜抢购成功");
                            }
                        }
                    }, i);
                    task.Start();
                }
                Console.WriteLine("主线程运行");
                Console.ReadLine();
            }
        }
    

      

    异步内的局部锁

    添加备注:3、创建不唯一,无论异步线程进入锁,其他异步必须等待。

        class Program
        {
            static object varlock = new object();//一、静态变量
            static void Main(string[] args)
            {
                for (int i = 0; i < 20; i++)
                {
                    Task task = new Task((obj) =>//标识线程id
                    {
                        Model1 se = new Model1();//在写代码时特别注意:同一个上下文实例,不在多个线程中使用
                        Phone phone;
                        lock (varlock)   //二、加锁 仅是让异步方法内操作的时候去排队,仅仅与当前函数并行,从而不影响下行语句的执行,相当于异步处理
                        {
                            phone = se.Phones.Where(a => a.Id == 1).FirstOrDefault();//表为复数                         
                            Console.WriteLine("异步查询的手机数量:" + phone.PCount + "--线程的ID" + obj);
                        }
                        if (phone.PCount < 1)
                        {
                            Console.WriteLine("秒杀已经结束");
                        }
                        else
                        {
                            Console.WriteLine("恭喜抢购成功");//我提示先放在前面检验结果
                            //第一个还没来及删减库存,就可以能已经有几个异步陆续进去else里面,
                            //1、因锁而让异步线程陆续去执行,导致第二个或者第三个异步获取未改变数量的时候执行到else里。
                            //即锁释放了,创建运行第二个异步查询过程中比第一个异步处理过程中快,也可能第三个异步创建运行,第一个异步处理还没有处理完,毕竟lock的处理低代码量少。                                                  
                            phone.PCount--; //减去库存                        
                            User_X_Phone uxp = new User_X_Phone() { PhoneId = 1 };//添加一个抢成功的人
                            uxp.UserId = Guid.NewGuid().ToString();
                            se.User_X_Phone.Add(uxp);
                            se.SaveChanges();
                        }
                    }, i);
                    task.Start();
                }
                Console.WriteLine("主线程运行");
                Console.ReadLine();
            }
        }
    

      

  • 相关阅读:
    Apache 安装后Error 403的故障排错方法(linux)
    ab接口压力测试工具使用
    php工具、拓展下载地址
    Jboss反序列化漏洞复现(CVE-2017-12149)
    Apache SSI 远程命令执行漏洞复现
    apache httpd多后缀解析漏洞复现
    IIS短文件名漏洞复现
    nginx文件名逻辑漏洞_CVE-2013-4547漏洞复现
    nginx CRLF(换行回车)注入漏洞复现
    nginx目录穿越漏洞复现
  • 原文地址:https://www.cnblogs.com/fger/p/10783567.html
Copyright © 2020-2023  润新知