一、线程实践步骤
建立数据表 提供两个版本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(); } }