Tiếp nối bài viết trước, chúng ta sẽ sử dụng một ví dụ để hiểu rõ hơn về async await không đồng bộ song song, bất đồng bộ song song và điều khiển đồng thời bất đồng bộ song song trong C#.
Vài ngày trước, tôi đã viết hai blog về C# async await asynchrony. Blog đầu tiên có rất nhiều độc giả và nhận được rất nhiều lượt thích và bình luận. Tôi nghĩ mọi người đều nên hiểu và nó tương đối đơn giản. Blog thứ hai nhận được ít lượt xem, ít lượt thích và không có bình luận nào.
Tôi rất bối rối. Blog thứ hai là trọng tâm. Mã tuyệt vời như vậy mà không ai bình luận về nó. Ngay cả những người giỏi nhất trong vòng tròn .NET cũng chưa từng viết đoạn mã này. Tại sao tôi lại nói vậy? Nó liên quan đến cú pháp sugar của C# async await: Ngay cả khi không có cú pháp sugar, đoạn mã vẫn có thể được viết theo cùng một cách. Ngay cả khi không có cú pháp sugar, Java8 vẫn có thể tạo ra đoạn mã tuyệt vời. Nhưng với cú pháp sugar async await của C#, các lập trình viên kinh doanh thông thường, thậm chí là người mới vào nghề, có thể viết mã có thông lượng cao và hiệu suất cao. Đây chính là ý nghĩa! Cho nên tôi nói rằng những người giỏi nhất chưa bao giờ viết về điều đó, vì họ có trình độ cao, có trí tuệ tốt, có nhiều phương tiện, nên tự nhiên họ không cần phải viết về điều đó. Nhưng nếu các lập trình viên bình thường viết code như vậy thì sẽ rất rắc rối và lỗi sẽ thường xuyên xảy ra. Tôi đã sử dụng từ "khám phá" trong tiêu đề. Có cách thực hành nào tốt hơn mà ngay cả người mới bắt đầu cũng có thể viết về lập trình song song và không đồng bộ không?
Hiệu suất ElasticSearch
Giá trị thực tế của mã này là để truy vấn es. Gần đây tôi thấy hiệu suất của es rất tốt! Đầu tiên, tôi sẽ cho bạn xem ảnh chụp màn hình đầu ra của giao diện điều khiển. Tôi triển khai dịch vụ trên một máy chủ, một môi trường thực, không phải trên máy tính của tôi. Truy vấn 379 es chỉ mất 0,185 giây. Tất nhiên, thời gian thực hiện sẽ dao động. Vài phần mười giây là bình thường và có thể vượt quá 1 giây. Điều es sợ nhất là các truy vấn chậm, truy vấn có điều kiện phức tạp và truy vấn phạm vi. Chiến lược của tôi là thực hiện nhiều truy vấn chính xác, có thể tận dụng được thông lượng cực cao của es.
Nhanh thế nào?
- Ảnh chụp màn hình ở trên chỉ là một trong những bài kiểm tra và phạm vi thời gian của phân tích truy vấn tương đối nhỏ (khối lượng dữ liệu hơn một tháng)
- Một giao diện dịch vụ khác phân tích dữ liệu trong nửa năm, tương đương khoảng 7,2 tỷ + 1,8 tỷ = 9 tỷ. Chỉ mất khoảng 3-6 giây để có được kết quả từ dữ liệu này.
Tại sao lại nhanh thế?
- Cụm es có nhiều máy chủ và bộ nhớ lớn (300G, tất nhiên trên máy chủ không chỉ có es) và bản thân cụm có thông lượng cao.
- Hiệu suất cao không đồng bộ song song và thông lượng cao! Cú pháp C# giúp việc viết bất đồng bộ song song trở nên dễ dàng.
Song song Không đồng bộ
Vì có nhiều truy vấn nên luồng đơn hoặc đồng bộ hóa chắc chắn là không đủ và phải được thực hiện song song. Python có thể viết mã song song không? Có thể viết được Java không? Tất nhiên là có thể! Nhưng đồng nghiệp cũ của tôi đã viết mã đồng bộ cho nhiều truy vấn tới es trong Python, vậy tại sao không song song hóa nó? Chắc chắn là có thể viết song song, nhưng tại sao không? Bởi vì nó phức tạp và khó viết. Bạn nghĩ mình có kỹ năng và trí tuệ tốt, nhưng người khác thì sao? Ý nghĩa của việc này là gì? Chúng ta không chỉ nên viết mã song song mà còn phải viết một cách đơn giản mà không phá hủy cấu trúc logic ban đầu của mã.
Phương pháp không đồng bộ
Mọi người đều có thể viết, chỉ cần sử dụng async await, rất đơn giản, đây là những gì tôi đã viết, mã chủ yếu tạo nhiều yêu cầu bất đồng bộ trong một vòng lặp kép (chỉ cần xem qua và bỏ qua):
/// /// xxx query/// public async Task<>> Query2(string strStartTime, string strEndTime, int kpCountThreshold, int countThreshold, int distanceThreshold, int timeThreshold, List peopleClusterList) { List resultList = new List(); Stopwatch sw = Stopwatch.StartNew(); //Tạo từ điển Dictionary clusterIdPeopleDict = new Dictionary(); foreach (PeopleCluster peopleCluster trong peopleClusterList) { foreach (string clusterId trong peopleCluster.ClusterIds) { if (!clusterIdPeopleDict.ContainsKey(clusterId)) { clusterIdPeopleDict.Add(clusterId, peopleCluster); } } } int queryCount = 0; Dictionary dict = new Dictionary(); foreach (PeopleCluster people1 trong peopleClusterList) { List peopleFeatureList = await ServiceFactory.Get().Query(strStartTime, strEndTime, people1); queryCount++; foreach (PeopleFeatureInfo peopleFeatureInfo1 trong peopleFeatureList) { DateTime capturedTime = DateTime.ParseExact(peopleFeatureInfo1.captured_time, "yyyyMMddHHmmss", CultureInfo.InvariantCulture); string strStartTime2 = capturedTime.AddSeconds(-timeThreshold).ToString("yyyyMMddHHmmss"); string strEndTime2 = capturedTime.AddSeconds(timeThreshold).ToString("yyyyMMddHHmmss"); List peopleFeatureList2 = await ServiceFactory.Get().QueryExcludeSelf(strStartTime2, strEndTime2, people1); queryCount++; if (peopleFeatureList2.Count > 0) { foreach (PeopleFeatureInfo peopleFeatureInfo2 trong peopleFeatureList2) { chuỗi khóa = null; PeopleCluster people2 = null; chuỗi people2ClusterId = null; if (clusterIdPeopleDict.ContainsKey(peopleFeatureInfo2.cluster_id.ToString())) { people2 = clusterIdPeopleDict[peopleFeatureInfo2.cluster_id.ToString()]; key = $"{string.Join(",", people1.ClusterIds)}_{string.Join(",", people2.ClusterIds)}"; } else { people2ClusterId = peopleFeatureInfo2.cluster_id.ToString(); key = $"{string.Join(",", people1.ClusterIds)}_{string.Join(",", people2ClusterId)}"; } double distance = LngLatUtil.CalcDistance(peopleFeatureInfo1.Longitude, peopleFeatureInfo1.Latitude, peopleFeatureInfo2.Longitude, peopleFeatureInfo2.Latitude); if (khoảng cách > distanceThreshold) tiếp tục; AccompanyInfo companionInfo; if (dict.ContainsKey(key)) { companionInfo = dict[key]; } else { companionInfo = new AccompanyInfo(); dict.Add(key, companionInfo); } companionInfo.People1 = people1; if (people2 != null) { companionInfo.People2 = people2; } else { companionInfo.ClusterId2 = people2ClusterId; } AccompanyItem companionItem = new AccompanyItem(); companionItem.Info1 = peopleFeatureInfo1; companionItem.Info2 = peopleFeatureInfo2; companionInfo.List.Add(accompanyItem); companionInfo.Count++; resultList.Add(accompanyInfo); } } } } resultList = resultList.FindAll(a => (a.People2 != null && a.Count >= kpCountThreshold) || a.Count >= countThreshold); // Xóa các phần tử trùng lặp int beforeDistinctCount = resultList.Count; resultList = resultList.DistinctBy(a => { string str1 = string.Join(",", a.People1.ClusterIds); string str2 = a.People2 != null ? string.Join(",", a.People2.ClusterIds) : string.Empty; string str3 = a.ClusterId2 ?? string.Empty; StringBuilder sb = new StringBuilder(); foreach (AccompanyItem item in a.List) { var info2 = item.Info2; sb.Append($"{info2.camera_id},{info2.captured_time},{info2.cluster_id}"); } return $"{str1}_{str2}_{str3}_{sb}"; }).ToList(); sw.Stop(); string msg = $"xxx truy vấn, thời gian sử dụng: {sw.Elapsed.TotalSeconds:0.000} giây, số truy vấn: {queryCount}, loại bỏ trùng lặp: {beforeDistinctCount}-->{resultList.Count}"; Console.WriteLine(msg); LogUtil.Info(msg); return resultList; }
Song song hóa các phương pháp không đồng bộ
Đoạn mã trên thì ổn nhưng cũng có vấn đề. Tức là nhiều yêu cầu được thực hiện trong một vòng lặp kép. Mặc dù sử dụng async await nhưng nó không song song và sẽ mất nhiều thời gian. Làm thế nào để tối ưu hóa nó? Vui lòng xem đoạn mã sau:
/// /// xxx query/// public async Task<>> Query(string strStartTime, string strEndTime, int kpCountThreshold, int countThreshold, int distanceThreshold, int timeThreshold, List peopleClusterList) { List resultList = new List(); Stopwatch sw = Stopwatch.StartNew(); //Tạo từ điển Dictionary clusterIdPeopleDict = new Dictionary(); foreach (PeopleCluster peopleCluster in peopleClusterList) { foreach (string clusterId in peopleCluster.ClusterIds) { if (!clusterIdPeopleDict.ContainsKey(clusterId)) { clusterIdPeopleDict.Add(clusterId, peopleCluster); } } } //Tổ chức lớp đầu tiên của tác vụ vòng lặp Dictionary<>>> tasks1 = new Dictionary<>>>(); foreach (PeopleCluster people1 in peopleClusterList) { var task1 = ServiceFactory.Get().Query(strStartTime, strEndTime, people1); tasks1.Add(people1, task1); } //Tính toán tác vụ vòng lặp cấp độ đầu tiên và lưu vào bộ nhớ đệm kết quả, sắp xếp tác vụ vòng lặp cấp độ thứ hai Dictionary<>>> tasks2 = new Dictionary<>>>(); Dictionary> cache1 = new Dictionary>(); foreach (PeopleCluster people1 in peopleClusterList) { List peopleFeatureList = await tasks1[people1]; cache1.Add(people1, peopleFeatureList); foreach (PeopleFeatureInfo peopleFeatureInfo1 trong peopleFeatureList) { DateTime capturedTime = DateTime.ParseExact(peopleFeatureInfo1.captured_time, "yyyyMMddHHmmss", CultureInfo.InvariantCulture); chuỗi strStartTime2 = capturedTime.AddSeconds(-timeThreshold).ToString("yyyyMMddHHmmss"); chuỗi strEndTime2 = capturedTime.AddSeconds(timeThreshold).ToString("yyyyMMddHHmmss"); var task2 = ServiceFactory.Get().QueryExcludeSelf(strStartTime2, strEndTime2, people1); chuỗi task2Key = $"{strStartTime2}_{strEndTime2}_{string.Join(",", people1.ClusterIds)}"; task2.TryAdd(task2Key, task2); } } //Đọc kết quả được lưu trong bộ nhớ đệm của tác vụ vòng lặp lớp đầu tiên và tính toán tác vụ vòng lặp lớp thứ hai Dictionary dict = new Dictionary(); foreach (PeopleCluster people1 in peopleClusterList) { List peopleFeatureList = cache1[people1]; foreach (PeopleFeatureInfo peopleFeatureInfo1 in peopleFeatureList) { DateTime capturedTime = DateTime.ParseExact(peopleFeatureInfo1.captured_time, "yyyyMMddHHmmss", CultureInfo.InvariantCulture); string strStartTime2 = capturedTime.AddSeconds(-timeThreshold).ToString("yyyyMMddHHmmss"); string strEndTime2 = capturedTime.AddSeconds(timeThreshold).ToString("yyyyMMddHHmmss"); string task2Key = $"{strStartTime2}_{strEndTime2}_{chuỗi.Join(",, people1.ClusterIds)}"; Danh sách peopleFeatureList2 = await tasks2[task2Key]; nếu (peopleFeatureList2.Count > 0) { foreach (PeopleFeatureInfo peopleFeatureInfo2 trong peopleFeatureList2) { chuỗi khóa = null; PeopleCluster people2 = null; chuỗi people2ClusterId = null; nếu (clusterIdPeopleDict.ContainsKey(peopleFeatureInfo2.cluster_id.ToString())) { people2 = clusterIdPeopleDict[peopleFeatureInfo2.cluster_id.ToString()]; khóa = $"{string.Join(",", people1.ClusterIds)}_{string.Join(",", people2.ClusterIds)}"; } nếu không { people2ClusterId = peopleFeatureInfo2.cluster_id.ToString(); khóa = $"{string.Join(",", companionInfo = new AccompanyInfo(); dict.Add(key, companionInfo); } companionInfo.People1 = people1; nếu (people2 != null) { companionInfo.People2 = people2; } else { companionInfo.ClusterId2 = people2ClusterId; } AccompanyItem companionItem = new AccompanyItem(); companionItem.Info1 = peopleFeatureInfo1; companionItem.Info2 = peopleFeatureInfo2; companionInfo.List.Add(accompanyItem); companionInfo.Count++; resultList.Add(accompanyInfo); } } } } resultList = resultList.FindAll(a => (a.People2 != null && a.Count >= kpCountThreshold) || a.Count >= countThreshold); // Xóa các phần tử trùng lặp int beforeDistinctCount = resultList.Count; resultList = resultList.DistinctBy(a => { string str1 = string.Join(",", a.People1.ClusterIds); string str2 = a.People2 != null ? string.Join(",", a.People2.ClusterIds) : string.Empty; string str3 = a.ClusterId2 ?? string.Empty; StringBuilder sb = new StringBuilder(); foreach (AccompanyItem item in a.List) { var info2 = item.Info2; sb.Append($"{info2.camera_id},{info2.captured_time},{info2.cluster_id}"); } return $"{str1}_{str2}_{str3}_{sb}"; }).ToList(); //Sắp xếp foreach (mục AccompanyInfo trong resultList) { item.List.Sort((a, b) => -string.Compare(a.Info1.captured_time, b.Info1.captured_time)); } sw.Stop(); string msg = $"xxx truy vấn, thời gian sử dụng: {sw.Elapsed.TotalSeconds:0.000} giây, số lượng truy vấn: {tasks1.Count + tasks2.Count}, loại bỏ trùng lặp: {beforeDistinctCount}-->{resultList.Count}"; Console.WriteLine(msg); LogUtil.Info(msg); return resultList; }AccompanyInfo companionInfo; nếu (dict.ContainsKey(key)) { companionInfo = dict[key]; } else { companionInfo = new AccompanyInfo; nếu (dict.ContainsKey(key)) { companionInfo = dict[key]; } else { companionInfo = new AccompanyInfo; nếu (dict.ContainsKey(key)) { companionInfo = dict[key]; } else { companionInfo = new AccompanyInfo; nếu (dict.ContainsKey(key)) { companionInfo = dict[key]; } // 添加 đi kèm với POCOMPANYINFO (); ); = null && a.count> = kpcountthreshold) | | = a.People2 != null ? string.Join(",", a.People2.ClusterIds) : string.Empty; string str3 = a.ClusterId2 ?? string.Empty; StringBuilder sb = new StringBuilder(); foreach (AccompanyItem mục trong a.List) { var info2 = item.Info2; sb.Append($"{info2.camera_id},{info2.captured_time},{info2.cluster_id}"); } return $"{str1}_{str2}_{str3}_{sb}"; }).ToList(); //Sắp xếpforeach (AccompanyInfo mục trong resultList) { item.List.Sort((a, b) => -string.Compare(a.Info1.captured_time, b.Info1.captured_time)); } sw.Stop(); string msg = $"xxx truy vấn, thời gian sử dụng: {sw.Elapsed.TotalSeconds:0.000} giây, số lượng truy vấn: {tasks1.Count + tasks2.Count}, loại bỏ trùng lặp: {beforeDistinctCount}-->{resultList.Count}"; Console.WriteLine(msg); LogUtil.Info(msg); return resultList; }AccompanyInfo companionInfo; nếu (dict.ContainsKey(key)) { companionInfo = dict[key]; } else { companionInfo = new AccompanyInfo; nếu (dict.ContainsKey(key)) { companionInfo = dict[key]; } else { companionInfo = new AccompanyInfo; nếu (dict.ContainsKey(key)) { companionInfo = dict[key]; } else { companionInfo = new AccompanyInfo; nếu (dict.ContainsKey(key)) { companionInfo = dict[key]; } // 添加 đi kèm với POCOMPANYINFO (); ); = null && a.count> = kpcountthreshold) | | = a.People2 != null ? string.Join(",", a.People2.ClusterIds) : string.Empty; string str3 = a.ClusterId2 ?? string.Empty; StringBuilder sb = new StringBuilder(); foreach (AccompanyItem mục trong a.List) { var info2 = item.Info2; sb.Append($"{info2.camera_id},{info2.captured_time},{info2.cluster_id}"); } return $"{str1}_{str2}_{str3}_{sb}"; }).ToList(); //Sắp xếpforeach (AccompanyInfo mục trong resultList) { item.List.Sort((a, b) => -string.Compare(a.Info1.captured_time, b.Info1.captured_time)); } sw.Stop(); string msg = $"xxx truy vấn, thời gian sử dụng: {sw.Elapsed.TotalSeconds:0.000} giây, số lượng truy vấn: {tasks1.Count + tasks2.Count}, loại bỏ trùng lặp: {beforeDistinctCount}-->{resultList.Count}"; Console.WriteLine(msg); LogUtil.Info(msg); return resultList; }// pompanyItem đi kèm với nhau resultlist = resultlist.findall (a => fo.list.add (đi kèm); Ompanyitem (); People2! = NULL đi kèm = a.ClusterId2 ?? string.Empty; StringBuilder sb = new StringBuilder(); foreach (AccompanyItem item in a.List) { var info2 = item.Info2; sb.Append($"{info2.camera_id},{info2.captured_time},{info2.cluster_id}"); } return $"{str1}_{str2}_{str3}_{sb}"; }).ToList(); //Sortingforeach (AccompanyInfo item in resultList) { item.List.Sort((a, b) => -string.Compare(a.Info1.captured_time, b.Info1.captured_time)); } sw.Stop(); string msg = $"xxx truy vấn, tốn thời gian: {sw.Elapsed.TotalSeconds:0.000} giây, số lượng truy vấn: {tasks1.Count + tasks2.Count}, loại bỏ trùng lặp: {beforeDistinctCount}-->{resultList.Count}"; Console.WriteLine(msg); LogUtil.Info(msg); return resultList; }// pompanyItem đi kèm với nhau resultlist = resultlist.findall (a => fo.list.add (đi kèm); Ompanyitem (); People2! = NULL đi kèm = a.ClusterId2 ?? string.Empty; StringBuilder sb = new StringBuilder(); foreach (AccompanyItem item in a.List) { var info2 = item.Info2; sb.Append($"{info2.camera_id},{info2.captured_time},{info2.cluster_id}"); } return $"{str1}_{str2}_{str3}_{sb}"; }).ToList(); //Sortingforeach (AccompanyInfo item in resultList) { item.List.Sort((a, b) => -string.Compare(a.Info1.captured_time, b.Info1.captured_time)); } sw.Stop(); string msg = $"xxx truy vấn, tốn thời gian: {sw.Elapsed.TotalSeconds:0.000} giây, số lượng truy vấn: {tasks1.Count + tasks2.Count}, loại bỏ trùng lặp: {beforeDistinctCount}-->{resultList.Count}"; Console.WriteLine(msg); LogUtil.Info(msg); return resultList; }
Giải thích đoạn mã trên
- Để song song hóa hoạt động không đồng bộ, vòng lặp kép cần được viết ba lần. Lần đầu tiên, bạn chỉ cần viết lớp vòng lặp đầu tiên, lưu lớp thứ hai. Vòng lặp phụ thứ hai không có chức năng xử lý dữ liệu. Lần thứ ba là lần hoàn thiện nhất.
- So với mã bất đồng bộ thông thường, vòng lặp kép thứ nhất và thứ hai là bổ sung, còn cấu trúc mã của vòng lặp kép thứ ba giống với cấu trúc mã của mã bất đồng bộ thông thường.
- Khi viết, trước tiên bạn có thể viết một phương thức bất đồng bộ thông thường, sau đó chuyển đổi nó thành một phương thức bất đồng bộ song song.
Tại sao bạn lại nói rằng những người giỏi nhất trong giới .NET chưa viết về nó?
- Tôi không muốn khoe khoang nhưng không ai đọc blog của tôi hoặc thích nó sao? !
- Điều tuyệt vời là C#. Nhờ cú pháp sugar của C#, mã tuyệt vời có thể được viết một cách đơn giản, điều này khiến nó thực sự tuyệt vời.
- Tôi hy vọng ai đó có thể viết một bài thực hành tốt hơn và loại bỏ cách viết của tôi, vì đây là cách viết có thể kiểm soát được nhất mà tôi có thể nghĩ ra.
Nhiều người có thể viết mã song song, bao gồm Java và Python, nhưng câu hỏi đặt ra là, làm sao những lập trình viên doanh nghiệp bình thường không giỏi về mảng này có thể viết loại mã song song này mà không cần suy nghĩ?
Cách viết tệ nhất, chẳng hạn như CompletableFuture của Java, rất phức tạp khi kết hợp với mã nghiệp vụ. Tôi thực sự không thể viết mà không suy nghĩ.
Cách viết thứ hai là, ví dụ:
Danh sách[] listArray = await Task.WhenAll(tasks2.Values);
Trong một vòng lặp kép, làm thế nào để có được kết quả? Tất nhiên là bạn có thể viết, nhưng bạn phải suy nghĩ về cách viết, đúng không? Phương pháp viết của tôi có thể trực tiếp nhận được kết quả trong thân vòng lặp kép:
Danh sách danh sách = chờ nhiệm vụ2[task2Key];
- Chỉ đăng mã C# thì không thuyết phục. Một trong những đồng nghiệp của tôi rất giỏi viết Python. Nhiều mã khai thác mà anh ấy viết là song song. Đây là một đoạn mã:
def get_es_multiprocess(index_list, people_list, core_percent, rev_clusterid_idcard_dict): ''' Đọc dữ liệu es đa tiến trình, chuyển đổi thành toàn bộ khung dữ liệu, sắp xếp theo thời gian :return: Khung dữ liệu lớn hơn''' col_list = ["cluster_id", "camera_id", "captured_time"] pool = Pool(processes=int(mp.cpu_count() * core_percent)) input_list = [(i, people_list, col_list) for i in index_list] res = pool.map(get_es, input_list) if not res: return None pool.close() pool.join() df_all = pd.DataFrame(columns=col_list+['longitude', 'latitude']) for df in res: df_all = pd.concat([df_all, df]) # Buộc chuyển đổi thành chuỗi tại đây! # todo df_all['cluster_id'] = df_all['cluster_id'].apply(lambda x: rev_clusterid_idcard_dict[str(x)]) del df_all['cluster_id'] df_all.rename(columns={'cluster_id_': 'cluster_id'}, inplace=True) df_all.sort_values(by='captured_time', inplace=True) print('=' * 100) print('Toàn bộ dữ liệu (trước khi phân cụm):') print(df_all.info()) cluster_id_list = [(i, df) for i, df in df_all.groupby(['cluster_id'])] cluster_id_list_split = [j for j in func(cluster_id_list, 1000000)] # todo Giảm bớt tập dữ liệu để gỡ lỗi! data_all = df_all.iloc[:, :] trả về data_all, cluster_id_list_split
Phân tích mã python ở trên
- Mã lõi:
res = pool.map(get_es, input_list) pool.join() ...bỏ qua
Trong số đó, get_es là phương thức truy vấn es. Nó không nên là phương thức bất đồng bộ, nhưng đây không phải là vấn đề. 2. res là kết quả truy vấn, được kiểm tra song song, đưa vào res, sau đó giải quyết kết quả. 3. Lưu ý rằng đây chỉ là một vòng lặp đơn. Hãy nghĩ về cách viết vòng lặp kép. 4. pool.join() chặn luồng hiện tại, điều này không tốt. 5. Đồng nghiệp của tôi đã viết "multi-process" trong bình luận. Có sai không? Nó có thực sự đa luồng không? Hay chỉ là quá trình đa xử lý? 6. Tất nhiên, Python có async await asynchronous writing, không tệ hơn C#. Chỉ là đồng nghiệp của tôi không sử dụng nó, có lẽ vì phiên bản Python anh ấy sử dụng không đủ mới. 7. Có quá nhiều chuỗi trong mã Python và chuỗi là khó bảo trì nhất. Tất cả các chuỗi trong C# đều được định kiểu mạnh.
Biến công việc trí óc thành công việc chân tay
Theo cùng một mô hình, tôi đã biến công việc trí óc thành công việc thực tế và viết một phương pháp khác (logic kinh doanh không quan trọng, chỉ cần xem xét việc sử dụng tính năng bất đồng bộ song song):
/// /// xxx query/// public async Task<>> Query(string strStartTime, string strEndTime, int kpCountThreshold, int timeThreshold, List peopleClusterList) { List resultList = new List(); Stopwatch sw = Stopwatch.StartNew(); //Sắp xếp lớp đầu tiên của các tác vụ vòng lặp, kiểm tra xxx Dictionary<>>> tasks1 = new Dictionary<>>>(); foreach (PeopleCluster people1 in peopleClusterList) { var task1 = ServiceFactory.Get().Query(strStartTime, strEndTime, people1); tasks1.Add(people1, task1); } //Tính toán tác vụ vòng lặp cấp độ đầu tiên và lưu trữ kết quả, sắp xếp tác vụ vòng lặp cấp độ thứ hai và tìm kiếm chính xác xxx Dictionary<>>> tasks2 = new Dictionary<>>>(); Dictionary> cache1 = new Dictionary>(); foreach (PeopleCluster people1 in peopleClusterList) { List peopleFeatureList = await tasks1[people1]; cache1.Add(people1, peopleFeatureList); foreach (PeopleFeatureInfo peopleFeatureInfo1 in peopleFeatureList) { string task2Key = $"{peopleFeatureInfo1.camera_id}_{peopleFeatureInfo1.captured_time}"; var task2 = ServiceFactory.Get().QueryExact(peopleFeatureInfo1.camera_id, peopleFeatureInfo1.captured_time); tasks2.TryAdd(task2Key, task2); } } //Đọc kết quả được lưu trong bộ nhớ đệm của tác vụ vòng lặp cấp độ đầu tiên và tính toán tác vụ vòng lặp cấp độ thứ hai Dictionary dictPersonVehicle = new Dictionary(); foreach (PeopleCluster people1 in peopleClusterList) { List peopleFeatureList = cache1[people1]; foreach (PeopleFeatureInfo peopleFeatureInfo1 in peopleFeatureList) { string task2Key = $"{peopleFeatureInfo1.camera_id}_{peopleFeatureInfo1.captured_time}"; List motorVehicleList = await tasks2[task2Key]; motorVehicleList = motorVehicleList.DistinctBy(a => a.plate_no).ToList(); foreach (MotorVehicleInfo motorVehicleInfo trong motorVehicleList) { PersonVehicleKey key = new PersonVehicleKey(people1, motorVehicleInfo.plate_no); PersonVehicleInfo personVehicleInfo; if (dictPersonVehicle.ContainsKey(key)) { personVehicleInfo = dictPersonVehicle[key]; } else { personVehicleInfo = new PersonVehicleInfo() { People = people1, PlateNo = motorVehicleInfo.plate_no, List = new List() }; dictPersonVehicle.Add(key, personVehicleInfo); } personVehicleInfo.List.Add(peopleFeatureInfo1); } } } //Sửa đổi xxx List keys = dictPersonVehicle.Keys.ToList(); Dictionary dict = new Dictionary(); for (int i = 0; i < keys.Count - 1; i++) { đối với (int j = i + 1; j < keys.Đếm; j++) { var key1 = keys[i]; var key2 = keys[j]; var personVehicle1 = dictPersonVehicle[key1]; var personVehicle2 = dictPersonVehicle[key2]; if (key1.PlateNo == key2.PlateNo) { foreach (PeopleFeatureInfo peopleFeature1 trong personVehicle1.List) { double minTimeDiff = double.MaxValue; int minIndex = -1; for (int k = 0; k < personVehicle2.List.Count; k++) { PeopleFeatureInfo peopleFeature2 = personVehicle2.List[k]; DateTime capturedTime1 = DateTime.ParseExact(peopleFeature1.captured_time, "yyyyMMddHHmmss", CultureInfo.InvariantCulture); DateTime capturedTime2 = DateTime.ParseExact(peopleFeature2.captured_time, var timeDiff = Math.Abs(capturedTime2.Subtract(capturedTime1).TotalSeconds); if (timeDiff < minTimeDiff) { minTimeDiff = timeDiff; minIndex = k; } } if (minIndex >= 0 && minTimeDiff <= timeThreshold * 60) { PeopleCluster people1 = key1.People; PeopleCluster people2 = key2.People; PeopleFeatureInfo peopleFeatureInfo2 = personVehicle2.List[minIndex]; string key = $"{string.Join(",", people1.ClusterIds)}_{string.Join(",", people2.ClusterIds)}"; ; SameVehicleInfo companionInfo; if (dict.ContainsKey(key)) { companionInfo = dict[key]; } else { companionInfo = // 删除重 int beforedistinceCount = resultList.Count; beforedistinceCount = resultList.Count; Count = resultList.Count; string.Join(",", a.People1.ClusterIds); string str2 = string.Join(",", a.People2.ClusterIds); StringBuilder sb = new StringBuilder(); foreach (mục SameVehicleItem trong a.List) { var info2 = item.Info2; sb.Append($"{info2.camera_id},{info2.captured_time},{info2.cluster_id}"); } return $"{str1}_{str2}_{sb}"; }).ToList(); //Sắp xếpforeach (mục SameVehicleInfo trong resultList) { item.List.Sort((a, b) => -string.Compare(a.Info1.captured_time, b.Info1.captured_time)); } sw.Stop(); string msg = $"xxx truy vấn, thời gian sử dụng: {sw.Elapsed.TotalSeconds:0.000} giây, số lượng truy vấn: {tasks1.Count + tasks2.Count}, loại bỏ trùng lặp: {beforeDistinctCount}-->{resultList.Count}"; Console.WriteLine(msg); LogUtil.Info(msg); return resultList; }} } nếu (minIndex >= 0 && minTimeDiff ) { minTimeDiff = timeDiff; minIndex = k; } } } } <= timeThreshold * 60) { PeopleCluster people1 = key1.People; PeopleCluster people2 = key2.People; PeopleFeatureInfo peopleFeatureInfo2 = personVehicle2.List[minIndex]; string key = $"{string.Join(",", people1.ClusterIds)}_{string.Join(",", people2.ClusterIds)}"; ; SameVehicleInfo companionInfo; if (dict.ContainsKey(key)) { companionInfo = dict[key]; } else { companionInfo = new SameVehicleInfo(); dict.Add(key, companionInfo); } companionInfo.People1 = people1; companionInfo.People2 = people2; SameVehicleItem companionItem = new SameVehicleItem(); companionItem.Info1 = peopleFeature1; companionItem.Info2 = peopleFeatureInfo2; companionInfo.List.Add(accompanyItem); companionInfo.Count++; resultList.Add(accompanyInfo); } } } } } resultList = resultList.FindAll(a => a.Count >= kpCountThreshold); //Lọc, loại trừ xxx resultList = resultList.FindAll(a => { if (string.Join(",", a.People1.ClusterIds) == string.Join(",", a.People2.ClusterIds)) { return false; } return true; }); //Xóa các phần tử trùng lặp int beforeDistinctCount = resultList.Count; resultList = resultList.DistinctBy(a => { string str1 = string.Join(",", a.People1.ClusterIds); string str2 = string.Join(",", a.People2.ClusterIds); StringBuilder sb = new StringBuilder(); foreach (SameVehicleItem item in a.List) { var info2 = item.Info2; sb.Append($"{info2.camera_id},{info2.captured_time},{info2.cluster_id}"); } return $"{str1}_{str2}_{sb}"; }).ToList(); //Sắp xếp foreach (mục SameVehicleInfo trong resultList) { item.List.Sort((a, b) => -string.Compare(a.Info1.captured_time, b.Info1.captured_time)); } sw.Stop(); string msg = $"xxx truy vấn, thời gian sử dụng: {sw.Elapsed.TotalSeconds:0.000} giây, số lượng truy vấn: {tasks1.Count + tasks2.Count}, loại bỏ trùng lặp: {beforeDistinctCount}-->{resultList.Count}"; Console.WriteLine(msg); LogUtil.Info(msg); return resultList; }} } nếu (minIndex >= 0 && minTimeDiff ) { minTimeDiff = timeDiff; minIndex = k; } } } } <= timeThreshold * 60) { PeopleCluster people1 = key1.People; PeopleCluster people2 = key2.People; PeopleFeatureInfo peopleFeatureInfo2 = personVehicle2.List[minIndex]; string key = $"{string.Join(",", people1.ClusterIds)}_{string.Join(",", people2.ClusterIds)}"; ; SameVehicleInfo companionInfo; if (dict.ContainsKey(key)) { companionInfo = dict[key]; } else { companionInfo = new SameVehicleInfo(); dict.Add(key, companionInfo); } companionInfo.People1 = people1; companionInfo.People2 = people2; SameVehicleItem companionItem = new SameVehicleItem(); companionItem.Info1 = peopleFeature1; companionItem.Info2 = peopleFeatureInfo2; companionInfo.List.Add(accompanyItem); companionInfo.Count++; resultList.Add(accompanyInfo); } } } } } resultList = resultList.FindAll(a => a.Count >= kpCountThreshold); //Lọc, loại trừ xxx resultList = resultList.FindAll(a => { if (string.Join(",", a.People1.ClusterIds) == string.Join(",", a.People2.ClusterIds)) { return false; } return true; }); //Xóa các phần tử trùng lặp int beforeDistinctCount = resultList.Count; resultList = resultList.DistinctBy(a => { string str1 = string.Join(",", a.People1.ClusterIds); string str2 = string.Join(",", a.People2.ClusterIds); StringBuilder sb = new StringBuilder(); foreach (SameVehicleItem item in a.List) { var info2 = item.Info2; sb.Append($"{info2.camera_id},{info2.captured_time},{info2.cluster_id}"); } return $"{str1}_{str2}_{sb}"; }).ToList(); //Sắp xếp foreach (mục SameVehicleInfo trong resultList) { item.List.Sort((a, b) => -string.Compare(a.Info1.captured_time, b.Info1.captured_time)); } sw.Stop(); string msg = $"xxx truy vấn, thời gian sử dụng: {sw.Elapsed.TotalSeconds:0.000} giây, số lượng truy vấn: {tasks1.Count + tasks2.Count}, loại bỏ trùng lặp: {beforeDistinctCount}-->{resultList.Count}"; Console.WriteLine(msg); LogUtil.Info(msg); return resultList; }Trừ (capturedTime1). Tổng giây); nếu (timeDiff < minTimeDiff) { minTimeDiff = timeDiff; minIndex = k; } } nếu (minIndex >= 0 && minTimeDiff <= timeThreshold * 60) { PeopleCluster people1 = key1.People; PeopleCluster people2 = key2.People; PeopleFeatureInfo peopleFeatureInfo2 = personVehicle2.List[minIndex]; chuỗi key = $"{string.Join(",", people1.ClusterIds)}_{string.Join(",", people2.ClusterIds)}"; ; SameVehicleInfo companionInfo; nếu (dict.ContainsKey(key)) { companionInfo = dict[key]; } else { companionInfo = new SameVehicleInfo(); dict.Add(key, companionInfo); } companionInfo.People1 = people1; companionInfo.People2 = people2; SameVehicleItem companionItem = new SameVehicleItem(); companionItem.Info1 = peopleFeature1; companionItem.Info2 = peopleFeatureInfo2; companionInfo.List.Add(accompanyItem); companionInfo.Count++; resultList.Add(accompanyInfo); } } } } } resultList = resultList.FindAll(a => a.Count >= kpCountThreshold); //Lọc, loại trừ xxx resultList = resultList.FindAll(a => { if (string.Join(",", a.People1.ClusterIds) == string.Join(",", a.People2.ClusterIds)) { return false; } return true; }); //Nhân đôi int beforeDistinctCount = resultList.Count; resultList = resultList.DistinctBy(a => { string str1 = string.Join(",", a.People1.ClusterIds); string str2 = string.Join(",", a.People2.ClusterIds); StringBuilder sb = new StringBuilder(); foreach (SameVehicleItem item in a.List) { var info2 = item.Info2; sb.Append($"{info2.camera_id},{info2.captured_time},{info2.cluster_id}"); } return $"{str1}_{str2}_{sb}"; }).ToList(); //Sortingforeach (SameVehicleInfo item in resultList) { item.List.Sort((a, b) => -string.Compare(a.Info1.captured_time, b.Info1.captured_time)); } sw.Stop(); string msg = $"xxx truy vấn, thời gian sử dụng: {sw.Elapsed.TotalSeconds:0.000} giây, số lượng truy vấn: {tasks1.Count + tasks2.Count}, loại bỏ trùng lặp: {beforeDistinctCount}-->{resultList.Count}"; Console.WriteLine(msg); LogUtil.Info(msg); return resultList; }Trừ (capturedTime1). Tổng giây); nếu (timeDiff < minTimeDiff) { minTimeDiff = timeDiff; minIndex = k; } } nếu (minIndex >= 0 && minTimeDiff <= timeThreshold * 60) { PeopleCluster people1 = key1.People; PeopleCluster people2 = key2.People; PeopleFeatureInfo peopleFeatureInfo2 = personVehicle2.List[minIndex]; chuỗi key = $"{string.Join(",", people1.ClusterIds)}_{string.Join(",", people2.ClusterIds)}"; ; SameVehicleInfo companionInfo; nếu (dict.ContainsKey(key)) { companionInfo = dict[key]; } else { companionInfo = new SameVehicleInfo(); dict.Add(key, companionInfo); } companionInfo.People1 = people1; companionInfo.People2 = people2; SameVehicleItem companionItem = new SameVehicleItem(); companionItem.Info1 = peopleFeature1; companionItem.Info2 = peopleFeatureInfo2; companionInfo.List.Add(accompanyItem); companionInfo.Count++; resultList.Add(accompanyInfo); } } } } } resultList = resultList.FindAll(a => a.Count >= kpCountThreshold); //Lọc, loại trừ xxx resultList = resultList.FindAll(a => { if (string.Join(",", a.People1.ClusterIds) == string.Join(",", a.People2.ClusterIds)) { return false; } return true; }); //Nhân đôi int beforeDistinctCount = resultList.Count; resultList = resultList.DistinctBy(a => { string str1 = string.Join(",", a.People1.ClusterIds); string str2 = string.Join(",", a.People2.ClusterIds); StringBuilder sb = new StringBuilder(); foreach (SameVehicleItem item in a.List) { var info2 = item.Info2; sb.Append($"{info2.camera_id},{info2.captured_time},{info2.cluster_id}"); } return $"{str1}_{str2}_{sb}"; }).ToList(); //Sortingforeach (SameVehicleInfo item in resultList) { item.List.Sort((a, b) => -string.Compare(a.Info1.captured_time, b.Info1.captured_time)); } sw.Stop(); string msg = $"xxx truy vấn, thời gian sử dụng: {sw.Elapsed.TotalSeconds:0.000} giây, số lượng truy vấn: {tasks1.Count + tasks2.Count}, loại bỏ trùng lặp: {beforeDistinctCount}-->{resultList.Count}"; Console.WriteLine(msg); LogUtil.Info(msg); return resultList; }Count >= kpCountThreshold); //Lọc và loại trừ xxx resultList = resultList.FindAll(a => { if (string.Join(",", a.People1.ClusterIds) == string.Join(",", a.People2.ClusterIds)) { return false; } return true; }); //Xóa các phần tử trùng lặp int beforeDistinctCount = resultList.Count; resultList = resultList.DistinctBy(a => { string str1 = string.Join(",", a.People1.ClusterIds); string str2 = string.Join(",", a.People2.ClusterIds); StringBuilder sb = new StringBuilder(); foreach (SameVehicleItem item in a.List) { var info2 = item.Info2; sb.Append($"{info2.camera_id},{info2.captured_time},{info2.cluster_id}"); } return $"{str1}_{str2}_{sb}"; }).ToList(); //Sắp xếp foreach (mục SameVehicleInfo trong resultList) { item.List.Sort((a, b) => -string.Compare(a.Info1.captured_time, b.Info1.captured_time)); } sw.Stop(); string msg = $"xxx truy vấn, thời gian sử dụng: {sw.Elapsed.TotalSeconds:0.000} giây, số lượng truy vấn: {tasks1.Count + tasks2.Count}, loại bỏ trùng lặp: {beforeDistinctCount}-->{resultList.Count}"; Console.WriteLine(msg); LogUtil.Info(msg); return resultList; }Count >= kpCountThreshold); //Lọc và loại trừ xxx resultList = resultList.FindAll(a => { if (string.Join(",", a.People1.ClusterIds) == string.Join(",", a.People2.ClusterIds)) { return false; } return true; }); //Xóa các phần tử trùng lặp int beforeDistinctCount = resultList.Count; resultList = resultList.DistinctBy(a => { string str1 = string.Join(",", a.People1.ClusterIds); string str2 = string.Join(",", a.People2.ClusterIds); StringBuilder sb = new StringBuilder(); foreach (SameVehicleItem item in a.List) { var info2 = item.Info2; sb.Append($"{info2.camera_id},{info2.captured_time},{info2.cluster_id}"); } return $"{str1}_{str2}_{sb}"; }).ToList(); //Sắp xếp foreach (mục SameVehicleInfo trong resultList) { item.List.Sort((a, b) => -string.Compare(a.Info1.captured_time, b.Info1.captured_time)); } sw.Stop(); string msg = $"xxx truy vấn, thời gian sử dụng: {sw.Elapsed.TotalSeconds:0.000} giây, số lượng truy vấn: {tasks1.Count + tasks2.Count}, loại bỏ trùng lặp: {beforeDistinctCount}-->{resultList.Count}"; Console.WriteLine(msg); LogUtil.Info(msg); return resultList; }
Chưa hoàn thành, cần bổ sung
XXX
- Nền tảng mã nguồn thấp mà chúng tôi phát triển thật tuyệt vời, C#: Tôi là người mã nguồn thấp!
- Nền tảng chúng tôi phát triển rất mạnh mẽ. Nó hỗ trợ viết script và tùy chỉnh script. C#: Tôi là script!
- Chúng tôi sử dụng spark và flink distributed computing, có hiệu suất tuyệt vời. C#: hiệu suất không đồng bộ song song thật tuyệt vời. Nếu bạn cung cấp thêm bộ nhớ, một máy duy nhất sẽ làm được. C#: Hiệu suất không đồng bộ song song của tôi có thể vượt trội hơn es, miễn là nó không tốn nhiều tài nguyên tính toán và có đủ bộ nhớ, không cần spark hay flink. Nó đơn giản trên một máy duy nhất, miễn là es là một cụm.
Cuối cùng, bài viết này về Exploration: Elegantly Implementing Parallelization of Asynchronous Methods đã có tại đây. Nếu bạn muốn biết thêm về Exploration: Elegantly Implementing Parallelization of Asynchronous Methods, vui lòng tìm kiếm các bài viết trên CFSDN hoặc tiếp tục duyệt các bài viết liên quan. Tôi hy vọng bạn sẽ ủng hộ blog của tôi trong tương lai! .
Tôi là một lập trình viên xuất sắc, rất giỏi!