在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
这个示例主要演示的是在多进程操作数据库时,如何避免并发重复数据入库的例子。 过多的线程理论不再阐述,网上、书上皆有。 项目采用 Asp.Net Framework 4.5 / Mysql 5.4 数据库。
首先创建一个 LockInsertDB.cs 文件,这个文件用来实现数据库添加;
1 using System; 2 using MySql; 3 using MySql.Data.MySqlClient; 4 using System.Collections.Generic; 5 using System.Linq; 6 using System.Text; 7 using System.Threading.Tasks; 8 9 namespace DBImportTool 10 { 11 public class LockInsertDB : Common 12 { 13 private object obj = new object(); 14 /// <summary> 15 /// 添加测试数据 16 /// </summary> 17 /// <param name="path">字段内容</param> 18 /// <param name="tname">线程名称</param> 19 public void InsertTestDB(string path, object tname) 20 { 21 MySql.Data.MySqlClient.MySqlConnection conn = new MySql.Data.MySqlClient.MySqlConnection("Server=10.17.1.57; Database=videodb; Uid=root; Pwd=xingzhi"); 22 try 23 { 24 conn.Open();//打开连接 25 MySql.Data.MySqlClient.MySqlCommand cmd = new MySql.Data.MySqlClient.MySqlCommand(); 26 cmd.Connection = conn; 27 //加入多线程,排它锁机制. 28 lock (obj) 29 { 30 string select = "SELECT * FROM `tbl_test` WHERE `filepath` = '" + path + "'"; 31 cmd.CommandText = select; 32 object val = cmd.ExecuteScalar(); 33 if (val == null) 34 { 35 string sql = "INSERT INTO `tbl_test` (`filepath`, `addtime`) VALUES ('" + path + "', NOW());"; 36 cmd.CommandText = sql; 37 cmd.ExecuteNonQuery(); 38 P(DateTime.Now.ToString() + "\t" + tname + "\tOK\t" + path + "\n"); 39 } 40 else 41 { 42 P(DateTime.Now.ToString() + "\t" + tname + "\t已存在\t" + path + "\n"); 43 } 44 } 45 } 46 catch 47 { 48 49 } 50 finally 51 { 52 //关闭连接 53 conn.Close(); 54 } 55 } 56 /// <summary> 57 /// 测试输出 58 /// </summary> 59 /// <param name="str">输出内容字符串</param> 60 public void P(string str) 61 { 62 Console.Write(str); 63 } 64 } 65 }
再创建一个控制台工程,Main方法代码如下:
1 using System; 2 using System.Threading; 3 using System.IO; 4 using System.Collections.Generic; 5 using System.Linq; 6 using System.Text; 7 using System.Threading.Tasks; 8 namespace DBImportTool 9 { 10 public class Mcqueen 11 { 12 static void Main(string[] args) 13 { 14 Run(); 15 Console.ReadLine(); 16 } 17 /// <summary> 18 /// 进程数量. 19 /// </summary> 20 static int tnum = 2; 21 /// <summary> 22 /// 所对象 23 /// </summary> 24 static object obj = new object(); 25 /// <summary> 26 /// 线程数组. 27 /// </summary> 28 static Thread[] threadarr = new Thread[tnum]; 29 /// <summary> 30 /// 数据库插入对象. 31 /// </summary> 32 static LockInsertDB mdb = new LockInsertDB(); 33 /// <summary> 34 /// 运行函数 35 /// </summary> 36 public static void Run() 37 { 38 for (int i = 0; i < tnum; i++) 39 { 40 Thread t = new Thread(T1); 41 threadarr[i] = t; 42 threadarr[i].Name = "Thread " + i.ToString() + " :"; 43 } 44 int y = 0; 45 while (y < tnum) 46 { 47 threadarr[y].Start(threadarr[y].Name); 48 y++; 49 } 50 } 51 /// <summary> 52 /// 读取磁盘文件路径. 53 /// </summary> 54 /// <param name="tname">线程名称</param> 55 public static void T1(object tname) 56 { 57 DirectoryInfo di = Directory.CreateDirectory(@"F:\邢智的文件\"); 58 GetFiles(di, tname); 59 } 60 /// <summary> 61 /// 采用对列入栈模式对列遍历文件夹结构. 62 /// </summary> 63 /// <param name="di">目录对象</param> 64 /// <param name="tobj">线程名称</param> 65 private static void GetFiles(DirectoryInfo di,object tobj) 66 { 67 int sgin = 1; 68 Stack<DirectoryInfo> stack_dir = new Stack<DirectoryInfo>(); 69 DirectoryInfo dir = di; 70 stack_dir.Push(di); 71 while (stack_dir.Count != 0) 72 { 73 dir = stack_dir.Pop(); 74 DirectoryInfo[] diarr = dir.GetDirectories(); 75 foreach (DirectoryInfo d in diarr) 76 stack_dir.Push(d); 77 FileInfo[] files = dir.GetFiles(); 78 foreach (FileInfo f in files) 79 { 80 mdb.InsertTestDB(f.FullName.Replace("\\", "\\\\"), tobj); 81 Thread.Sleep(200); 82 sgin++; 83 } 84 } 85 } 86 public static void P(string str) 87 { 88 Console.Write(str); 89 } 90 } 91 } static LockInsertDB mdb = new LockInsertDB(); 一开始把这句初始化对象放到了private static void GetFiles(DirectoryInfo di,object tobj) 函数里面,虽然在后续的执行过程中没有任何问题, 但是初始化运行线程时导致了进程并发进入lock锁中,后来研究一下原因,是因为每个进程都在实例化化它的时候都会在lock中分配一个标识符, 这样一来,假如A进程内实例化了A1,那么当B进程进来之后,B却不认识A线程内的A1,因此lock也就对A1不起作用了。 因此把这个类跳出线程而放到一个全局中去实例化,也就不存在这个lock失效的问题了。
运行结果如下:
如果需要模拟更多的进程用户,只需要把 tnum = 2 的数值改一下即可。 |
请发表评论