欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

基于C#解決庫(kù)存扣減及訂單創(chuàng)建時(shí)防止并發(fā)死鎖的問題

 更新時(shí)間:2022年05月27日 14:04:12   作者:tansar  
這篇文章主要介紹了基于C#解決庫(kù)存扣減及訂單創(chuàng)建時(shí)防止并發(fā)死鎖的問題,很多開發(fā)人員對(duì)于這個(gè)問題的排查起來是比較困難的,而生產(chǎn)生的原因多種多樣,很多人認(rèn)是因?yàn)楸碇械臄?shù)據(jù)太多了同時(shí)操作的人多人才會(huì)產(chǎn)生這種錯(cuò)誤,下面我們來還原一下死鎖的過程

解決庫(kù)存扣減及訂單創(chuàng)建時(shí)防止并發(fā)死鎖的問題

在我們?nèi)粘i_發(fā)的過程可有會(huì)遇到以下錯(cuò)誤

事務(wù)(進(jìn)程 ID 82)與另一個(gè)進(jìn)程被死鎖在 鎖 資源上,并且已被選作死鎖犧牲品。請(qǐng)重新運(yùn)行該事務(wù)

很多開發(fā)人員對(duì)于這個(gè)問題的排查起來是比較困難的,而生產(chǎn)生的原因多種多樣,很多人認(rèn)是因?yàn)楸碇械臄?shù)據(jù)太多了同時(shí)操作的人多人才會(huì)產(chǎn)生這種錯(cuò)誤,下面我們來還原一下死鎖的過程。

我們看一下以下sql代碼(該樣例代碼測(cè)試環(huán)境為SqlServer)

1. 第一先創(chuàng)建一個(gè)測(cè)試表H_Test

復(fù)制以下代碼

  SET ANSI_NULLS ON
  GO
  SET QUOTED_IDENTIFIER ON
  GO
  CREATE TABLE [dbo].[H_TEST](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [DID] [int] NULL,
    [UNAME] [nvarchar](50) NULL,
    [UNAME2] [nvarchar](50) NULL,
  CONSTRAINT [PK_H_TEST_3994ceeb-a4b8-41e1-b06b-1e59a2e51d8c] PRIMARY KEY CLUSTERED 
  (
    [Id] ASC
  )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
  ) ON [PRIMARY]
  GO
  EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'自增主鍵' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_TEST', @level2type=N'COLUMN',@level2name=N'Id'
  GO
  EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'DID' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_TEST', @level2type=N'COLUMN',@level2name=N'DID'
  GO
  EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'UNAME' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_TEST', @level2type=N'COLUMN',@level2name=N'UNAME'
  GO
  EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'UNAME2' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_TEST', @level2type=N'COLUMN',@level2name=N'UNAME2'
  GO
  insert [dbo].[H_TEST](DID,UNAME,UNAME2) VALUES(1,'HI','HI2');
  insert [dbo].[H_TEST](DID,UNAME,UNAME2) VALUES(2,'HISQL','HISQL2');

2. 打開兩個(gè)查詢窗口

在窗口1中復(fù)制以下代碼

 begin tran 
  update dbo.H_TEST 
  set UNAME='d1' 
  where dID=1 
  waitfor delay '00:00:10' 
  update H_TEST 
  set UNAME='d2' 
  where dID=2
  commit tran 

在窗口2中復(fù)制以下代碼

begin tran 
  update H_TEST 
  set UNAME='d2' 
  where dID=2
  waitfor delay '00:00:10' 
  update dbo.H_TEST 
  set UNAME='d1' 
  where dID=1
  commit tran 

3. 執(zhí)行代碼

同時(shí)執(zhí)行窗口1和窗口2的代碼,在等待一段時(shí)間后你就可以看到以下錯(cuò)誤如下所示

通過以上的測(cè)試就還原了產(chǎn)生死鎖的過程,剛才的測(cè)試表H_Test中只有兩條數(shù)據(jù),其實(shí)產(chǎn)生死鎖與數(shù)據(jù)大小沒有很大的關(guān)系,其實(shí)與整個(gè)事務(wù)的執(zhí)行長(zhǎng)短有關(guān)系,兩個(gè)業(yè)務(wù)都在操作同一條數(shù)據(jù),且一個(gè)事務(wù)中包含非常復(fù)雜的處理邏輯且執(zhí)行時(shí)間比較長(zhǎng)那么在并發(fā)或相對(duì)較多的業(yè)務(wù)操作時(shí)就會(huì)產(chǎn)生死鎖。

那么怎樣解決死鎖?

1. 減少事務(wù)的執(zhí)行時(shí)間。

優(yōu)化代碼將不需要包在事務(wù)的邏輯分離出來以減少鎖的占用時(shí)間.可以減少一部分的死鎖,但在高并發(fā)操作時(shí)依然會(huì)產(chǎn)生死鎖

2. 業(yè)務(wù)鎖

日常我們用到的鎖都是高度依賴于數(shù)據(jù)來鎖定來保證數(shù)據(jù)的原子性問題,但這樣有一個(gè)很大的BUG就是對(duì)數(shù)據(jù)庫(kù)的性能壓力非常大,在出現(xiàn)高并發(fā)時(shí)可能應(yīng)用扛得住數(shù)據(jù)庫(kù)扛不住的情況

下面介紹的就是基于HiSql 的業(yè)務(wù)鎖機(jī)制解決死鎖問題,我們模擬一種場(chǎng)景 扣減庫(kù)存并生成訂單那么我們模擬創(chuàng)建兩張表 庫(kù)存表H_Stock 及訂單表H_Order 表創(chuàng)建的sql如下

HiSql怎樣使用 請(qǐng)參照hisql快速上手

庫(kù)存表sql代碼

CREATE TABLE [dbo].[H_Stock](
    [Batch] [varchar](20) NOT NULL,
    [Material] [varchar](20) NOT NULL,
    [Location] [varchar](5) NULL,
    [st_kc] [decimal](18, 2) NULL,
    [CreateTime] [datetime] NULL,
    [CreateName] [nvarchar](50) NULL,
    [ModiTime] [datetime] NULL,
    [ModiName] [nvarchar](50) NULL,
  CONSTRAINT [PK_H_Stock] PRIMARY KEY CLUSTERED 
  (
    [Batch] ASC,
    [Material] ASC
  )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
  ) ON [PRIMARY]
  GO

  ALTER TABLE [dbo].[H_Stock] ADD  CONSTRAINT [DF_H_Stock_st_kc]  DEFAULT ((0)) FOR [st_kc]
  GO

  ALTER TABLE [dbo].[H_Stock] ADD  CONSTRAINT [DF_H_Stock_CreateTime]  DEFAULT (getdate()) FOR [CreateTime]
  GO

  ALTER TABLE [dbo].[H_Stock] ADD  CONSTRAINT [DF_H_Stock_CreateName]  DEFAULT ('') FOR [CreateName]
  GO

  ALTER TABLE [dbo].[H_Stock] ADD  CONSTRAINT [DF_H_Stock_ModiTime]  DEFAULT (getdate()) FOR [ModiTime]
  GO

  ALTER TABLE [dbo].[H_Stock] ADD  CONSTRAINT [DF_H_Stock_ModiName]  DEFAULT ('') FOR [ModiName]
  GO

  EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'批次號(hào)' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_Stock', @level2type=N'COLUMN',@level2name=N'Batch'
  GO

  EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'款號(hào)' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_Stock', @level2type=N'COLUMN',@level2name=N'Material'
  GO

  EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'庫(kù)位' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_Stock', @level2type=N'COLUMN',@level2name=N'Location'
  GO

  EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'庫(kù)存數(shù)' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_Stock', @level2type=N'COLUMN',@level2name=N'st_kc'
  GO

  EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'創(chuàng)建時(shí)間' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_Stock', @level2type=N'COLUMN',@level2name=N'CreateTime'
  GO

  EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'創(chuàng)建人' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_Stock', @level2type=N'COLUMN',@level2name=N'CreateName'
  GO

  EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'修改時(shí)間' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_Stock', @level2type=N'COLUMN',@level2name=N'ModiTime'
  GO

  EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'修改人' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_Stock', @level2type=N'COLUMN',@level2name=N'ModiName'
  GO

訂單表sql

 CREATE TABLE [dbo].[H_Order](
    [OrderId] [bigint] NOT NULL,
    [Batch] [varchar](20) NOT NULL,
    [Material] [varchar](20) NOT NULL,
    [Shop] [varchar](5) NULL,
    [Location] [varchar](5) NULL,
    [SalesNum] [decimal](18, 2) NULL,
    [CreateTime] [datetime] NULL,
    [CreateName] [nvarchar](50) NULL,
    [ModiTime] [datetime] NULL,
    [ModiName] [nvarchar](50) NULL,
  CONSTRAINT [PK_H_Order] PRIMARY KEY CLUSTERED 
  (
    [OrderId] ASC,
    [Batch] ASC,
    [Material] ASC
  )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
  ) ON [PRIMARY]
  GO

  ALTER TABLE [dbo].[H_Order] ADD  CONSTRAINT [DF_H_Order_SalesNum]  DEFAULT ((0)) FOR [SalesNum]
  GO

  ALTER TABLE [dbo].[H_Order] ADD  CONSTRAINT [DF_H_Order_CreateTime]  DEFAULT (getdate()) FOR [CreateTime]
  GO

  ALTER TABLE [dbo].[H_Order] ADD  CONSTRAINT [DF_H_Order_CreateName]  DEFAULT ('') FOR [CreateName]
  GO

  ALTER TABLE [dbo].[H_Order] ADD  CONSTRAINT [DF_H_Order_ModiTime]  DEFAULT (getdate()) FOR [ModiTime]
  GO

  ALTER TABLE [dbo].[H_Order] ADD  CONSTRAINT [DF_H_Order_ModiName]  DEFAULT ('') FOR [ModiName]
  GO

  EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'批次號(hào)' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_Order', @level2type=N'COLUMN',@level2name=N'Batch'
  GO

  EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'款號(hào)' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_Order', @level2type=N'COLUMN',@level2name=N'Material'
  GO

  EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'門店' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_Order', @level2type=N'COLUMN',@level2name=N'Shop'
  GO

  EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'出庫(kù)庫(kù)位' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_Order', @level2type=N'COLUMN',@level2name=N'Location'
  GO

  EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'銷售數(shù)量' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_Order', @level2type=N'COLUMN',@level2name=N'SalesNum'
  GO

  EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'創(chuàng)建時(shí)間' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_Order', @level2type=N'COLUMN',@level2name=N'CreateTime'
  GO

  EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'創(chuàng)建人' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_Order', @level2type=N'COLUMN',@level2name=N'CreateName'
  GO

  EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'修改時(shí)間' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_Order', @level2type=N'COLUMN',@level2name=N'ModiTime'
  GO

  EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'修改人' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'H_Order', @level2type=N'COLUMN',@level2name=N'ModiName'
  GO

測(cè)試場(chǎng)景

開啟多個(gè)線程隨機(jī)產(chǎn)生不同的訂單(一個(gè)訂單中有不同批次和數(shù)量)直至庫(kù)存扣減完成并檢測(cè)是否有鎖產(chǎn)生,且?guī)齑嬗袥]有少扣和超扣,如果達(dá)到這兩個(gè)目標(biāo)說明測(cè)試是成功的

c# 代碼

class Program
  {
    static void Main(string[] args)
    {
        Console.WriteLine("測(cè)試!");
        StockThread();
        var s = Console.ReadLine();
    }
    static void StockThread()
    {
        //如果有安裝redis可以啟用以下測(cè)試一下
        //HiSql.Global.RedisOn = true;//開啟redis緩存
        //HiSql.Global.RedisOptions = new RedisOptions { Host = "172.16.80.178", PassWord = "pwd123", Port = 6379, CacheRegion = "TST", Database = 0 };
        HiSqlClient sqlClient = Demo_Init.GetSqlClient();
        //清除庫(kù)存表和訂單表數(shù)據(jù)
        sqlClient.CodeFirst.Truncate("H_Stock");
        sqlClient.CodeFirst.Truncate("H_Order");
        //初始化庫(kù)存數(shù)據(jù)
        sqlClient.Modi("H_Stock", new List<object> {
            new { Batch="9000112112",Material="ST0021",Location="A001",st_kc=5000},
            new { Batch="8000252241",Material="ST0080",Location="A001",st_kc=1000},
            new { Batch="7000252241",Material="ST0026",Location="A001",st_kc=1500}
        }).ExecCommand();
        //第一種場(chǎng)景 一個(gè)訂單中只有一個(gè)批次
        string[] grp_arr1 = new string[] { "9000112112" };
        //第二種場(chǎng)景 一個(gè)訂單中有兩個(gè)批次
        string[] grp_arr2 = new string[] {  "8000252241" , "9000112112"   };
        //第三中場(chǎng)景一個(gè)訂單中有三個(gè)批次
        string[] grp_arr3 = new string[] { "8000252241", "9000112112", "7000252241" };
        Random random = new Random();
        HiSqlClient _sqlClient = Demo_Init.GetSqlClient();
        //表結(jié)構(gòu)緩存預(yù)熱
        var _dt1= _sqlClient.HiSql("select * from H_Order").Take(1).Skip(1).ToTable();
        var _dt2 = _sqlClient.HiSql("select * from H_Stock").Take(1).Skip(1).ToTable();
        //開啟10個(gè)線程運(yùn)行
        Parallel.For(0, 10, (index, y) => {
          int grpidx = index % 3;
          string[] grparr = null;
          if (grpidx == 0)
              grparr = grp_arr1;
          else if (grpidx == 1)
              grparr = grp_arr2;
          else
              grparr = grp_arr3;
          //Thread.Sleep(random.Next(10) * 200);
          Console.WriteLine($" {index}線程Id:{Thread.CurrentThread.ManagedThreadId}\t{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
          //執(zhí)行訂單創(chuàng)建
          var rtn = CreateSale(grparr);
          Console.WriteLine(rtn.Item2);
        });
    }

    static Tuple<bool, string> CreateSale(string[] grparr)
    {
        Random random = new Random();
        HiSqlClient _sqlClient = Demo_Init.GetSqlClient();
        bool _flag = true;
        Tuple<bool, string> rtn = new Tuple<bool, string>(true, "執(zhí)行");
        //指定雪花ID使用的引擎 (可以不指定)
        HiSql.Snowflake.SnowType = SnowType.IdWorker;
        //產(chǎn)生一個(gè)唯一的訂單號(hào)
        Int64 orderid = HiSql.Snowflake.NextId();
        //加鎖并執(zhí)行 將一個(gè)訂單的批次都加鎖防止同一時(shí)間被其它業(yè)務(wù)修改
        var _rtn = HiSql.Lock.LockOnExecute(grparr, () =>
        {
            //能執(zhí)行到此說明已經(jīng)加鎖成功(注:非數(shù)據(jù)庫(kù)級(jí)加鎖)
            DataTable dt = _sqlClient.HiSql($"select Batch,Material,Location,st_kc from H_Stock where  Batch in ({grparr.ToSqlIn()}) and st_kc>0").ToTable();
            if (dt.Rows.Count > 0)
            {
                List<object> lstorder = new List<object>();
                Console.WriteLine($"雪花ID{orderid}");
                string _shop = "4301";//門店編號(hào)
                _sqlClient.BeginTran();
                foreach (string n in grparr)
                {
                    int s = random.Next(1,10);
                    int v = _sqlClient.Update("H_Stock", new { st_kc = $"`st_kc`-{s}" }).Where($"Batch='{n}' and st_kc>={s}").ExecCommand();
                    if (v == 0)
                    {
                        _flag = false;
                        Console.WriteLine($"批次:[{n}]扣減[{s}]失敗");
                        rtn = new Tuple<bool, string>(false, $"批次:[{n}]庫(kù)存已經(jīng)不足");
                        _sqlClient.RollBackTran();
                        break;
                    }
                    else
                    {
                        DataRow _drow = dt.AsEnumerable().Where(s => s.Field<string>("Batch").Equals(n)).FirstOrDefault();
                        if (_drow != null)
                        {
                            lstorder.Add(
                                new
                                {
                                    OrderId = orderid,
                                    Batch = _drow["Batch"].ToString(),
                                    Material = _drow["Material"].ToString(),
                                    Shop = _shop,
                                    Location = _drow["Location"].ToString(),
                                    SalesNum = s,
                                }
                                );
                        }
                        else
                        {
                            _flag = false;
                            Console.WriteLine($"批次:[{n}]扣減[{s}]失敗 未找到庫(kù)存");
                            _sqlClient.RollBackTran();
                            break;
                        }
                    }
                }
                if (_flag)
                {
                    //生成訂單
                    if (lstorder.Count > 0)
                        _sqlClient.Insert("H_Order", lstorder).ExecCommand();
                    _sqlClient.CommitTran();
                }
            }
            else
            {
                Console.WriteLine($"庫(kù)存不足...");
                rtn = new Tuple<bool, string>(false, "庫(kù)存已經(jīng)不足");
            }
        }, new LckInfo
        {
            UName = "tanar",
            Ip = "127.0.0.1"
        }, 20, 10);//加鎖超時(shí)時(shí)間設(shè)定
        _sqlClient.Close();
        Console.WriteLine(_rtn.Item2);
        //可以注釋線程等待
        //Thread.Sleep(random.Next(1,10)*100);
        if (rtn.Item1)
            return CreateSale(grparr);
        else
            return rtn;
    }
  }

數(shù)據(jù)庫(kù)連接配置

 internal class Demo_Init
  {
      public static HiSqlClient GetSqlClient()
      {

          HiSqlClient sqlclient = new HiSqlClient(
                    new ConnectionConfig()
                    {
                        DbType = DBType.SqlServer,
                        DbServer = "local-HoneBI",
                        ConnectionString = "server=(local);uid=sa;pwd=Hone@123;database=HiSql;Encrypt=True; TrustServerCertificate=True;",//; MultipleActiveResultSets = true;
                        User = "tansar",//可以指定登陸用戶的帳號(hào)
                        Schema = "dbo",
                        IsEncrypt = true,
                        IsAutoClose = true,
                        SqlExecTimeOut = 60000,
                        AppEvents = new AopEvent()
                        {
                            OnDbDecryptEvent = (connstr) =>
                            {
                                //解密連接字段
                                //Console.WriteLine($"數(shù)據(jù)庫(kù)連接:{connstr}");
                                return connstr;
                            },
                            OnLogSqlExecuting = (sql, param) =>
                            {
                                //sql執(zhí)行前 日志記錄 (異步)
                                //Console.WriteLine($"sql執(zhí)行前記錄{sql} time:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff")}");
                            },
                            OnLogSqlExecuted = (sql, param) =>
                            {
                                //sql執(zhí)行后 日志記錄 (異步)
                                //Console.WriteLine($"sql執(zhí)行后記錄{sql} time:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff")}");
                            },
                            OnSqlError = (sqlEx) =>
                            {
                                //sql執(zhí)行錯(cuò)誤后 日志記錄 (異步)
                                Console.WriteLine(sqlEx.Message.ToString());
                            },
                            OnTimeOut = (int timer) =>
                            {
                                //Console.WriteLine($"執(zhí)行SQL語句超過[{timer.ToString()}]毫秒...");
                            }
                        }
                    }
                    );

          //sqlclient.CodeFirst.InstallHisql();
          return sqlclient;
      }
  }

通過查詢庫(kù)存和訂單信息核對(duì)庫(kù)存是否扣減正常

select * from H_Stock 
select batch,sum(salesnum) as salesnum from H_Order group by batch
select orderid,sum(salesnum) as salesnum from H_Order group by orderid
select * from H_Order

核驗(yàn)結(jié)果

通過測(cè)試過程可以發(fā)現(xiàn) 不會(huì)產(chǎn)生死鎖也不會(huì)造成庫(kù)存扣減異常保證了數(shù)據(jù)的一致性

到此這篇關(guān)于基于C#解決庫(kù)存扣減及訂單創(chuàng)建時(shí)防止并發(fā)死鎖的問題的文章就介紹到這了,更多相關(guān)c#庫(kù)存扣減防止并發(fā)死鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論