依星源码资源网,依星资源网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

【好消息,好消息,好消息】VIP会员可以发表文章赚积分啦 !
查看: 23|回复: 0

仅此一文让你明白事务隔离级别、脏读、不可重复读、幻读

[复制链接] 主动推送

1万

主题

1万

帖子

1万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
14786
发表于 前天 14:47 | 显示全部楼层 |阅读模式
仅此一文让你明白事务隔离级别、脏读、不可重复读、幻读
网络上关于这方面的博文有些偏理论,有些通篇代码,都不能深入浅出。本文用图文并茂的方式,配上行云流水般的代码,非要摆清楚这个问题。附件中包含下面相关代码。
事务是现代关系型数据库的核心之一。在多个事务并发操作数据库(多线程、网络并发等)的时候,如果没有有效的避免机制,就会出现以下几种问题:
第一类丢失更新(Lost Update)
在完全未隔离事务的情况下,两个事务更新同一条数据资源,某一事务完成,另一事务异常终止,回滚造成第一个完成的更新也同时丢失 。这个问题现代关系型数据库已经不会发生,就不在这里占用篇幅,有兴趣的可以自行百度。
脏读(Dirty Read)
A事务执行过程中,B事务读取了A事务的修改。但是由于某些原因,A事务可能没有完成提交,发生RollBack了操作,则B事务所读取的数据就会是不正确的。这个未提交数据就是脏读(Dirty Read)。脏读产生的流程如下:

仅此一文让你明白事务隔离级别、脏读、不可重复读、幻读

仅此一文让你明白事务隔离级别、脏读、不可重复读、幻读
可以用EF Core模拟此过程:
  1. class TestReadUncommitted :TestBase
  2.     {
  3.         private AutoResetEvent _autoResetEvent;

  4.         [Test]
  5.         public void ReadUncommitted()
  6.         {
  7.             using (var context = _autofacServiceProvider.GetService())
  8.             {
  9.                 var user = context.Users.SingleOrDefault(u => u.Account == "admin");
  10.                 Console.WriteLine([        DISCUZ_CODE_3        ]quot;初始用户状态:【{user.Status}】");
  11.             }

  12.             _autoResetEvent = new AutoResetEvent(false);
  13.             ThreadPool.QueueUserWorkItem(data =>{
  14.                 Write();  //启动线程写
  15.             });
  16.             ThreadPool.QueueUserWorkItem(data =>{
  17.                 Read();  //启动线程读
  18.             });

  19.             Thread.Sleep(5000);

  20.             using (var context = _autofacServiceProvider.GetService())
  21.             {
  22.                 var user = context.Users.SingleOrDefault(u => u.Account == "admin");
  23.                 Console.WriteLine([        DISCUZ_CODE_3        ]quot;最终用户状态:【{user.Status}】");
  24.             }
  25.         }

  26.         private void Read()
  27.         {
  28.             _autoResetEvent.WaitOne();

  29.             var options = new TransactionOptions { IsolationLevel = IsolationLevel.ReadUncommitted };
  30.             using (var scope = new TransactionScope(TransactionScopeOption.Required, options))
  31.             {
  32.                 using (var context = _autofacServiceProvider.GetService())
  33.                 {
  34.                     var user = context.Users.SingleOrDefault(u => u.Account == "admin");
  35.                     Console.WriteLine([        DISCUZ_CODE_3        ]quot;事务B:脏读到的用户状态:【{user.Status}】--{DateTime.Now.ToString("HH:mm:ss fff")}");
  36.                     //如果这时执行下面的判断
  37.                     if (user.Status == 1)
  38.                     {
  39.                         Console.WriteLine("事务B:非正常数据,会产生意想不到的BUG");
  40.                     }
  41.                 }
  42.             }
  43.         }
  44.         private void Write()
  45.         {
  46.             using (var scope = new TransactionScope(TransactionScopeOption.Required,
  47.                 new TransactionOptions {IsolationLevel = IsolationLevel.ReadCommitted}))
  48.             {
  49.                 Console.WriteLine([        DISCUZ_CODE_3        ]quot;事务A:修改--{DateTime.Now.ToString("HH:mm:ss fff")}");
  50.                 using (var context = _autofacServiceProvider.GetService())
  51.                 {
  52.                     var user = context.Users.SingleOrDefault(u => u.Account == "admin");
  53.                     user.Status = 1-user.Status;  //模拟修改
  54.                     context.SaveChanges();
  55.                 }

  56.                 _autoResetEvent.Set();  //模拟多线程切换,这时切换到Read线程,复现脏读

  57.                 Thread.Sleep(2000);  //模拟长事务
  58.                 Console.WriteLine([        DISCUZ_CODE_3        ]quot;事务A:改完,但没提交--{DateTime.Now.ToString("HH:mm:ss fff")}");
  59.             }
  60.         }
  61.     }

  62. 脏读示例
复制代码
对应的执行结果:

仅此一文让你明白事务隔离级别、脏读、不可重复读、幻读

仅此一文让你明白事务隔离级别、脏读、不可重复读、幻读
不可重复读(Nonrepeatable Read)
B事务读取了两次数据,在这两次的读取过程中A事务修改了数据,B事务的这两次读取出来的数据不一样。B事务这种读取的结果,即为不可重复读(Nonrepeatable Read)。不可重复读的产生的流程如下:

仅此一文让你明白事务隔离级别、脏读、不可重复读、幻读

仅此一文让你明白事务隔离级别、脏读、不可重复读、幻读
模拟代码如下:
  1. public class TestReadCommitted : TestBase
  2.     {
  3.         private AutoResetEvent _toWriteEvent = new AutoResetEvent(false);
  4.         private AutoResetEvent _toReadEvent = new AutoResetEvent(false);

  5.         [Test]
  6.         public void ReadCommitted()
  7.         {
  8.             ThreadPool.QueueUserWorkItem(data => {
  9.                 Read();  //启动线程读
  10.             });
  11.             ThreadPool.QueueUserWorkItem(data => {
  12.                 Write();  //启动线程写
  13.             });

  14.             Thread.Sleep(5000);

  15.             using (var context = _autofacServiceProvider.GetService())
  16.             {
  17.                 var user = context.Users.SingleOrDefault(u => u.Account == "admin");
  18.                 Console.WriteLine([        DISCUZ_CODE_9        ]quot;最终用户状态:【{user.Status}】--{DateTime.Now.ToString("HH:mm:ss fff")}");
  19.             }

  20.         }

  21.         private void Read()
  22.         {
  23.             using (var transactionScope = new TransactionScope(TransactionScopeOption.Required,
  24.                 new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
  25.             {
  26.                 using (var context = _autofacServiceProvider.GetService())
  27.                 {
  28.                     var user = context.Users.SingleOrDefault(u => u.Account == "admin");
  29.                     Console.WriteLine([        DISCUZ_CODE_9        ]quot;事务B:第一次读取:【{user.Status}】--{DateTime.Now.ToString("HH:mm:ss fff")}");
  30.                 }

  31.                 _toWriteEvent.Set();  //模拟多线程切换,这时切换到写线程,复现不可重复读
  32.                 _toReadEvent.WaitOne();

  33.                 using (var context = _autofacServiceProvider.GetService())
  34.                 {
  35.                     var user = context.Users.SingleOrDefault(u => u.Account == "admin");
  36.                     Console.WriteLine([        DISCUZ_CODE_9        ]quot;事务B:第二次读取:【{user.Status}】--{DateTime.Now.ToString("HH:mm:ss fff")}");
  37.                 }
  38.             }

  39.         }

  40.         private void Write()
  41.         {
  42.             _toWriteEvent.WaitOne();

  43.             using (var scope = new TransactionScope(TransactionScopeOption.Required,
  44.                 new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
  45.             {
  46.                 User user = null;
  47.                 using (var context = _autofacServiceProvider.GetService())
  48.                 {
  49.                     user = context.Users.SingleOrDefault(u => u.Account == "admin");
  50.                     Console.WriteLine([        DISCUZ_CODE_9        ]quot;事务A:读取为【{user?.Status}】--{DateTime.Now.ToString("HH:mm:ss fff")}");
  51.                     user.Status = 1 - user.Status;
  52.                     context.SaveChanges();
  53.                 }
  54.                 scope.Complete();
  55.                 Console.WriteLine([        DISCUZ_CODE_9        ]quot;事务A:已被更改为【{user.Status}】--{DateTime.Now.ToString("HH:mm:ss fff")}");
  56.                 _toReadEvent.Set();
  57.             }
  58.         }
  59.     }

  60. 不可重复读示例
复制代码
对应的执行结果:

仅此一文让你明白事务隔离级别、脏读、不可重复读、幻读

仅此一文让你明白事务隔离级别、脏读、不可重复读、幻读
不可重复读有一种特殊情况,两个事务更新同一条数据资源,后完成的事务会造成先完成的事务更新丢失。这种情况就是大名鼎鼎的第二类丢失更新。主流的数据库已经默认屏蔽了第一类丢失更新问题(即:后做的事务撤销,发生回滚造成已完成事务的更新丢失),但我们编程的时候仍需要特别注意第二类丢失更新。它产生的流程如下:

仅此一文让你明白事务隔离级别、脏读、不可重复读、幻读

仅此一文让你明白事务隔离级别、脏读、不可重复读、幻读
模拟代码如下:
  1. public class TestReadCommitted2 : TestBase
  2.     {
  3.         private AutoResetEvent _toWriteEvent = new AutoResetEvent(false);
  4.         private AutoResetEvent _toReadEvent = new AutoResetEvent(false);

  5.         [Test]
  6.         public void ReadCommitted()
  7.         {
  8.             ThreadPool.QueueUserWorkItem(data => {
  9.                 Read();  //启动线程读
  10.             });
  11.             ThreadPool.QueueUserWorkItem(data => {
  12.                 Write();  //启动线程写
  13.             });

  14.             Thread.Sleep(5000);

  15.             using (var context = _autofacServiceProvider.GetService())
  16.             {
  17.                 var user = context.Users.SingleOrDefault(u => u.Account == "admin");
  18.                 Console.WriteLine([        DISCUZ_CODE_19        ]quot;最终用户状态:【{user.Status}】--{DateTime.Now.ToString("HH:mm:ss fff")}");
  19.             }
  20.         }

  21.         private void Read()
  22.         {
  23.             using (var transactionScope = new TransactionScope(TransactionScopeOption.Required,
  24.                 new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
  25.             {
  26.                 User user = null;
  27.                 using (var context = _autofacServiceProvider.GetService())
  28.                 {
  29.                     user = context.Users.SingleOrDefault(u => u.Account == "admin");
  30.                     Console.WriteLine([        DISCUZ_CODE_19        ]quot;事务B:第一次读取:【{user?.Status}】--{DateTime.Now.ToString("HH:mm:ss fff")}");
  31.                 }

  32.                 _toWriteEvent.Set();  //模拟多线程切换,这时切换到写线程,复现不可重复读
  33.                 _toReadEvent.WaitOne();

  34.                 using (var context = _autofacServiceProvider.GetService())
  35.                 {
  36.                     user = context.Users.SingleOrDefault(u => u.Account == "admin");
  37.                     Console.WriteLine([        DISCUZ_CODE_19        ]quot;事务B:第二次读取:【{user?.Status}】--{DateTime.Now.ToString("HH:mm:ss fff")}");
  38.                     user.Status = 1 - user.Status;
  39.                     context.SaveChanges();
  40.                 }
  41.                 transactionScope.Complete();
  42.                 Console.WriteLine([        DISCUZ_CODE_19        ]quot;事务B:已被更改为【{user?.Status}】--{DateTime.Now.ToString("HH:mm:ss fff")}");
  43.             }

  44.         }

  45.         private void Write()
  46.         {
  47.             _toWriteEvent.WaitOne();

  48.             using (var scope = new TransactionScope(TransactionScopeOption.Required,
  49.                 new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
  50.             {
  51.                 User user = null;
  52.                 using (var context = _autofacServiceProvider.GetService())
  53.                 {
  54.                     user = context.Users.SingleOrDefault(u => u.Account == "admin");
  55.                     Console.WriteLine([        DISCUZ_CODE_19        ]quot;事务A:读取为【{user?.Status}】--{DateTime.Now.ToString("HH:mm:ss fff")}");
  56.                     user.Status = 1 - user.Status;
  57.                     context.SaveChanges();
  58.                 }
  59.                 scope.Complete();
  60.                 Console.WriteLine([        DISCUZ_CODE_19        ]quot;事务A:已被更改为【{user.Status}】--{DateTime.Now.ToString("HH:mm:ss fff")}");
  61.                 _toReadEvent.Set();
  62.             }
  63.         }
  64.     }

  65. 第二类更新丢失示例
复制代码
对应的执行结果如下图:

仅此一文让你明白事务隔离级别、脏读、不可重复读、幻读

仅此一文让你明白事务隔离级别、脏读、不可重复读、幻读
可以明显看出事务A的更新被事务B所覆盖,更新丢失。
幻读(Phantom Read)
B事务读取了两次数据,在这两次的读取过程中A事务添加了数据,B事务的这两次读取出来的集合不一样。幻读产生的流程如下:

仅此一文让你明白事务隔离级别、脏读、不可重复读、幻读

仅此一文让你明白事务隔离级别、脏读、不可重复读、幻读
这个流程看起来和不可重复读差不多,但幻读强调的集合的增减,而不是单独一条数据的修改。
模拟代码如下:
  1. public class TestRepeat : TestBase
  2.     {
  3.         private AutoResetEvent _toWriteEvent = new AutoResetEvent(false);
  4.         private AutoResetEvent _toReadEvent = new AutoResetEvent(false);

  5.         [Test]
  6.         public void Repeat()
  7.         {
  8.             ThreadPool.QueueUserWorkItem(data => {
  9.                 Read();  //启动线程读
  10.             });
  11.             ThreadPool.QueueUserWorkItem(data => {
  12.                 Write();  //启动线程写
  13.             });

  14.             Thread.Sleep(6000);

  15.         }

  16.         private void Read()
  17.         {
  18.             using (var transactionScope = new TransactionScope(TransactionScopeOption.Required,
  19.                 new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead }))
  20.             {
  21.                 using (var context = _autofacServiceProvider.GetService())
  22.                 {
  23.                     Console.WriteLine([        DISCUZ_CODE_41        ]quot;事务B:第一次读取:【{context.Users.Count()}】--{DateTime.Now.ToString("HH:mm:ss fff")}");
  24.                 }

  25.                 _toWriteEvent.Set();  //模拟多线程切换,这时切换到写线程,复现幻读
  26.                 _toReadEvent.WaitOne();

  27.                 using (var context = _autofacServiceProvider.GetService())
  28.                 {
  29.                     Console.WriteLine([        DISCUZ_CODE_41        ]quot;事务B:第二次读取:【{context.Users.Count()}】--{DateTime.Now.ToString("HH:mm:ss fff")}");
  30.                 }
  31.             }

  32.         }

  33.         private void Write()
  34.         {
  35.             _toWriteEvent.WaitOne();

  36.             using (var scope = new TransactionScope(TransactionScopeOption.Required,
  37.                      new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
  38.             {
  39.                 Console.WriteLine([        DISCUZ_CODE_41        ]quot;事务A:新增一条--{DateTime.Now.ToString("HH:mm:ss fff")}");
  40.                 using (var context = _autofacServiceProvider.GetService())
  41.                 {
  42.                     var id = GenerateId.ShortStr();
  43.                     context.Users.Add(new User { Id = id, Account = id, Status = 0, Name = id, CreateTime = DateTime.Now});
  44.                     context.SaveChanges();
  45.                 }
  46.                 scope.Complete();
  47.                 Console.WriteLine([        DISCUZ_CODE_41        ]quot;事务A:完成新增--{DateTime.Now.ToString("HH:mm:ss fff")}");
  48.                 _toReadEvent.Set();
  49.             }
  50.         }
  51.     }

  52. 幻读示例
复制代码
执行结果:

仅此一文让你明白事务隔离级别、脏读、不可重复读、幻读

仅此一文让你明白事务隔离级别、脏读、不可重复读、幻读
数据库隔离级别
为了解决上面提及的并发问题,主流关系型数据库都会提供四种事务隔离级别。
读未提交(Read Uncommitted)
在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别是最低的隔离级别,虽然拥有超高的并发处理能力及很低的系统开销,但很少用于实际应用。因为采用这种隔离级别只能防止第一类更新丢失问题,不能解决脏读,不可重复读及幻读问题。
读已提交(Read Committed)
这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别可以防止脏读问题,但会出现不可重复读及幻读问题。
可重复读(Repeatable Read)
这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。这种隔离级别可以防止除幻读外的其他问题。
可串行化(Serializable)
这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读、第二类更新丢失问题。在这个级别,可以解决上面提到的所有并发问题,但可能导致大量的超时现象和锁竞争,通常数据库不会用这个隔离级别,我们需要其他的机制来解决这些问题:乐观锁和悲观锁。
这四种隔离级别会产生的问题如下(网上到处都有,懒得画了):

仅此一文让你明白事务隔离级别、脏读、不可重复读、幻读

仅此一文让你明白事务隔离级别、脏读、不可重复读、幻读
如何使用数据库的隔离级别
很多文章博客在介绍完这些隔离级别以后,就没有以后了。读的人一般会觉得,嗯,是这么回事,我知道了!
学习一个知识点,是需要实践的。比如下面这个常见而又异常严重的情况:

仅此一文让你明白事务隔离级别、脏读、不可重复读、幻读

仅此一文让你明白事务隔离级别、脏读、不可重复读、幻读
图中是典型的第二类丢失更新问题,后果异常严重。我们这里就以读已提交(Read Committed)及以下隔离级别中会出现不可重复读现象为例。从上面的表格可以看出,当事务隔离级别为可重复读(Repeatable Read)时可以避免。把TestReadCommitted中的Read线程事务级别调整一下:
  1. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2. // file:        TestTestReadCommitted.cs
  3. //
  4. // summary:        读已提交会出现“不可重复读”现象
  5. //              把读线程(事务B)的隔离级别调整到RepeatableRead,即可杜绝
  6. ////////////////////////////////////////////////////////////////////////////////////////////////////

  7. using System;
  8. using System.Linq;
  9. using System.Threading;
  10. using System.Transactions;
  11. using Microsoft.EntityFrameworkCore;
  12. using Microsoft.Extensions.DependencyInjection;
  13. using NUnit.Framework;
  14. using TestTransaction.Domain;

  15. namespace TestTransaction.Test
  16. {
  17.     public class TestReadCommitted : TestBase
  18.     {
  19.         private AutoResetEvent _toWriteEvent = new AutoResetEvent(false);
  20.         private AutoResetEvent _toReadEvent = new AutoResetEvent(false);

  21.         [Test]
  22.         public void ReadCommitted()
  23.         {
  24.             ThreadPool.QueueUserWorkItem(data => {
  25.                 Read();  //启动线程读
  26.             });
  27.             ThreadPool.QueueUserWorkItem(data => {
  28.                 Write();  //启动线程写
  29.             });

  30.             Thread.Sleep(60000);

  31.             using (var context = _autofacServiceProvider.GetService())
  32.             {
  33.                 var user = context.Users.SingleOrDefault(u => u.Account == "admin");
  34.                 Console.WriteLine([        DISCUZ_CODE_59        ]quot;最终用户状态:【{user.Status}】--{DateTime.Now.ToString("HH:mm:ss fff")}");
  35.             }

  36.         }

  37.         private void Read()
  38.         {
  39.             //读线程(事务B)的隔离级别调整到RepeatableRead
  40.             using (var transactionScope = new TransactionScope(TransactionScopeOption.Required,
  41.                 new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead, Timeout = TimeSpan.FromSeconds(40) }))
  42.             {
  43.                 using (var context = _autofacServiceProvider.GetService())
  44.                 {
  45.                     var user = context.Users.SingleOrDefault(u => u.Account == "admin");
  46.                     Console.WriteLine([        DISCUZ_CODE_59        ]quot;事务B:第一次读取:【{user.Status}】--{DateTime.Now.ToString("HH:mm:ss fff")}");
  47.                 }

  48.                 _toWriteEvent.Set();  //模拟多线程切换,这时切换到写线程,复现不可重复读
  49.                 _toReadEvent.WaitOne();

  50.                 using (var context = _autofacServiceProvider.GetService())
  51.                 {
  52.                     var user = context.Users.SingleOrDefault(u => u.Account == "admin");
  53.                     Console.WriteLine([        DISCUZ_CODE_59        ]quot;事务B:第二次读取:【{user.Status}】--{DateTime.Now.ToString("HH:mm:ss fff")}");
  54.                 }
  55.             }

  56.         }

  57.         private void Write()
  58.         {
  59.             _toWriteEvent.WaitOne();

  60.             using (var scope = new TransactionScope(TransactionScopeOption.Required,
  61.                 new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted, Timeout = TimeSpan.FromSeconds(5) }))
  62.             {
  63.                 User user = null;
  64.                 using (var context = _autofacServiceProvider.GetService())
  65.                 {
  66.                     user = context.Users.SingleOrDefault(u => u.Account == "admin");
  67.                     Console.WriteLine([        DISCUZ_CODE_59        ]quot;事务A:读取为【{user?.Status}】--{DateTime.Now.ToString("HH:mm:ss fff")}");
  68.                     user.Status = 1 - user.Status;
  69.                     try
  70.                     {
  71.                         context.SaveChanges();
  72.                         scope.Complete();

  73.                         Console.WriteLine([        DISCUZ_CODE_59        ]quot;事务A:已被更改为【{user.Status}】--{DateTime.Now.ToString("HH:mm:ss fff")}");
  74.                     }
  75.                     catch (DbUpdateException e)
  76.                     {
  77.                         Console.WriteLine([        DISCUZ_CODE_59        ]quot;事务A:异常,为了保证可重复读,你的修改提交失败,请稍后重试--{DateTime.Now.ToString("HH:mm:ss fff")}");
  78.                     }
  79.                 }
  80.                 _toReadEvent.Set();
  81.             }
  82.         }
  83.     }
  84. }
复制代码
这时执行效果如下:
实际项目中,通过提示客户端重做的方式,完美解决了不可重复读的问题。其他并发问题,也可以通过类似的方式解决。
提取码下载:
文件名称:提取码下载.txt 
下载次数:0  文件大小:16 Bytes  售价:10金钱 [记录]
下载权限: 不限 [购买VIP]   [充值]   [在线充值]   【VIP会员6折;永久VIP4折】
安全检测,请放心下载




相关帖子

扫码关注微信公众号,及时获取最新资源信息!下载附件优惠VIP会员6折;永久VIP4折
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

免责声明:
1、本站提供的所有资源仅供参考学习使用,版权归原著所有,禁止下载本站资源参与商业和非法行为,请在24小时之内自行删除!
2、本站所有内容均由互联网收集整理、网友上传,并且以计算机技术研究交流为目的,仅供大家参考、学习,请勿任何商业目的与商业用途。
3、若您需要商业运营或用于其他商业活动,请您购买正版授权并合法使用。
4、论坛的所有内容都不保证其准确性,完整性,有效性,由于源码具有复制性,一经售出,概不退换。阅读本站内容因误导等因素而造成的损失本站不承担连带责任。
5、用户使用本网站必须遵守适用的法律法规,对于用户违法使用本站非法运营而引起的一切责任,由用户自行承担
6、本站所有资源来自互联网转载,版权归原著所有,用户访问和使用本站的条件是必须接受本站“免责声明”,如果不遵守,请勿访问或使用本网站
7、本站使用者因为违反本声明的规定而触犯中华人民共和国法律的,一切后果自己负责,本站不承担任何责任。
8、凡以任何方式登陆本网站或直接、间接使用本网站资料者,视为自愿接受本网站声明的约束。
9、本站以《2013 中华人民共和国计算机软件保护条例》第二章 “软件著作权” 第十七条为原则:为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。若有学员需要商用本站资源,请务必联系版权方购买正版授权!
10、本网站如无意中侵犯了某个企业或个人的知识产权,请来信【站长信箱312337667@qq.com】告之,本站将立即删除。
郑重声明:
本站所有资源仅供用户本地电脑学习源代码的内含设计思想和原理,禁止任何其他用途!
本站所有资源、教程来自互联网转载,仅供学习交流,不得商业运营资源,不确保资源完整性,图片和资源仅供参考,不提供任何技术服务。
本站资源仅供本地编辑研究学习参考,禁止未经资源商正版授权参与任何商业行为,违法行为!如需商业请购买各资源商正版授权
本站仅收集资源,提供用户自学研究使用,本站不存在私自接受协助用户架设游戏或资源,非法运营资源行为。
 
在线客服
点击这里给我发消息 点击这里给我发消息 点击这里给我发消息
售前咨询热线
312337667

微信扫一扫,私享最新原创实用干货

QQ|免责声明|小黑屋|依星资源网 ( 鲁ICP备2021043233号-3 )|网站地图

GMT+8, 2025-4-3 09:32

Powered by Net188.com X3.4

邮箱:312337667@qq.com 客服QQ:312337667(工作时间:9:00~21:00)

快速回复 返回顶部 返回列表