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