[C#]多线程写文件

C#的任务并行库

在.net4.0中,有了一个新的类库:任务并行库。它极大地简化了并行编程且内容丰富。

其中最简单的是Parallel.For循环和Parallel.ForEach循环。它们位于System.Threading.Tasks命名空间

它们是两个方法,这两个方法将迭代分别放在不同的处理器上并行处理,如果机器是多处理器或多核处理器,这样就会使性能大大提升。

换言之,不同于python,C#的多线程支持多核

应用场景

我的应用场景是,将cognex的DS1000扫出的idb格式的点云数据存到txt文件中

用多线程写数据到文本中可以跑满CPU,提高速度。

废话不多说,直接上代码。

示例代码(C#):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.IO;
using System.Diagnostics;

namespace CSharpCmd
{
class Program
{
// 读写锁,当资源处于写入模式时,其他线程写入需要等待本次写入结束之后才能继续写入
static ReaderWriterLockSlim LogWriteLock = new ReaderWriterLockSlim();

// 保存数据的子函数
static void IO_sub(List<double[]> dstPoint3D, int start, int end, string strPath)
{
string strData = "";

int countOutput = 0;
for (int i = start; i < end; i++)
{
double x = dstPoint3D[i][0];
double y = dstPoint3D[i][1];
double z = dstPoint3D[i][2];

// 点为(0,0,0)时不输出
if (x == 0 && y == 0 && z == 0)
{
;
}
else
{
countOutput = countOutput + 1;
strData += x.ToString("F3") + ',' + y.ToString("F3") + ',' + z.ToString("F3") + Environment.NewLine;
}

// 满1000个点输出,或到最后一个点时输出
if ((countOutput == 1000) || (i == end - 1))
{
// 设置读写锁为写入模式独占资源,其他写入请求需要等待本次写入结束之后才能继续写入
LogWriteLock.EnterWriteLock();
// 每1000个点保存1次数据到txt
System.IO.File.AppendAllText(strPath, strData);
// 退出写入模式,释放资源占用
LogWriteLock.ExitWriteLock();
strData = "";
countOutput = 0;
}
}
}

// 串行保存数据到txt
static void IO_Sequential(List<double[]> dstPoint3D)
{
string strPath = @"C:\Users\xgy\Desktop\cognex导出点云\Data_Sequential.txt";
IO_sub(dstPoint3D, 0, dstPoint3D.Count, strPath);
}

// 并行保存数据到txt
static void IO_Parallel(List<double[]> dstPoint3D)
{
string strPath = @"C:\Users\xgy\Desktop\cognex导出点云\Data_Parallel.txt";
int NUM = Environment.ProcessorCount; // 4 处理器数量
Parallel.For(0, NUM, k =>
{
int start = dstPoint3D.Count / NUM * k;
int end = dstPoint3D.Count / NUM * (k + 1); // start<=i<end
IO_sub(dstPoint3D, start, end, strPath);
}); // Parallel.For
}

public static void Main()
{
// 生成30w个数据,作为点云数据存在变量中
List<double[]> dstPoint3D = new List<double[]>();
for (int rrow = 0; rrow < 500; rrow++)
{
for (int ccol = 0; ccol < 600; ccol++)
{
double[] point3D = new double[3];
point3D[0] = ccol;
point3D[1] = rrow;
point3D[2] = 0;
dstPoint3D.Add(point3D);
}
}

// 串行保存数据到txt
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
IO_Sequential(dstPoint3D);
stopwatch.Stop();
Console.Error.WriteLine("Sequential loop time in milliseconds: {0}", stopwatch.ElapsedMilliseconds);

// 并行保存数据到txt
stopwatch.Reset();
stopwatch.Start();
IO_Parallel(dstPoint3D);
stopwatch.Stop();
Console.Error.WriteLine("Parallel loop time in milliseconds: {0}", stopwatch.ElapsedMilliseconds);

Console.ReadKey();
}
}
}

看程序的运行结果,由于并行的顺序是不确定的,故串行和并行的总结果数相同,但顺序不同。

可能是由于读写锁限制了性能,并行只比串行稍微快一点点。

参考

参考内容 参考方面
C#使用读写锁三行代码简单解决多线程并发写入文件时线程同步的问题 读写文件,包含多线程Parallel.For、文件锁ReaderWriterLockSlim
C# Parallel之for,foreach使用(笔记) Parallel.For()、Parallel.ForEach()的最简示例
改善C#程序的建议10:用Parallel简化Task Parallel.For()、Parallel.ForEach()、Parallel.Invoke()的最简示例
C# 并行任务——Parallel类 Parallel.For()、Parallel.ForEach()、Parallel.Invoke()的最简示例
如何:编写简单的 Parallel.For 循环 微软官方技术文档,“.NET 中的并行编程”