在.Net Framework 4.0之前,为解决集合的安全问题,主要用到的方法是锁。举一个例子。假若计算机的某一个文件夹中存在1000张图片,现在要对这1000张图片做缩略图处理,把处理好的缩略图保存到另外一个文件夹中。
先写一个处理图片缩略图的类,代码图下所示:
public class OperPicClass
{
/// <summary>
/// 输入jpg文件
/// </summary>
public string InFileName { get; set; }
/// <summary>
/// 输出jpg缩略图的文件
/// </summary>
public string OutFileName { get; set; }
public OperPicClass()
{
}
/// <summary>
/// 处理缩略图
/// </summary>
/// <returns></returns>
public bool Exc()
{
//string pDir = System.IO.Path.GetDirectoryName(OutFileName);
//if (!System.IO.Directory.Exists(pDir))
//{
// System.IO.Directory.CreateDirectory(pDir);
//}
Console.WriteLine("处理图片:" + Thread.CurrentThread.Name + "||" + InFileName);
return true;
}
}
单线程情况下,对1000张图片的处理如下:
private void btRun_Click(object sender, EventArgs e)
{
//单线程下,图片生成缩略图
List<OperPicClass> listJpgFiles = new List<OperPicClass>();
for (int i = 0; i < 1000; i++)
{
listJpgFiles.Add(new OperPicClass() { InFileName = "文件路径", OutFileName = "输出路径" });
}
listJpgFiles.ForEach((o) => { o.Exc(); });
}
单线程下,会导致1000张图片处理时间长。分析1000张图片做缩略图,整个过程互不干扰,考虑用多线程的方式,减少处理时间。在.Net Framework 4.0出现之前,为了保证集合安全,需要重写一个安全的集合类,如下所示:
public class MutilThreadOperClass<T> : List<T> where T : new()
{
private readonly object _lock;
public MutilThreadOperClass()
{
_lock = new object();
}
public new void Add(T t)
{
lock (_lock)
{
base.Add(t);
}
}
private int _index = 0;
public T getObject()
{
T t = default(T);
if (_index >= 0 && _index < this.Count)
{
lock (_lock)
{
t = this[_index];
_index++;
}
}
return t;
}
}
集合类写好后,多线程处理如下所示:
private void btRun_Click(object sender, EventArgs e)
{
MutilThreadOperClass<OperPicClass> tMutilThreadOperClass = new MutilThreadOperClass<OperPicClass>();
for (int i = 0; i < 10000; i++)
{
tMutilThreadOperClass.Add(new OperPicClass() { InFileName = "文件路径" + i, OutFileName = "输出路径" });
}
Thread[] tThread = new Thread[8];
for (int i = 0; i < tThread.Length; i++)
{
tThread[i] = new Thread(() =>
{
while (true)
{
Thread.Sleep(10);
try
{
OperPicClass d = tMutilThreadOperClass.getObject();
d.Exc();
}
catch
{ break; }
}
});
tThread[i].IsBackground = true;
tThread[i].Name = "我是线程" + i;
tThread[i].Start();
}
}
.Net Framework 4.0出现之后,微软提供了System.Collections.Concurrent命名空间,在此命名空间中,有以下类:
类说明 | ||
---|---|---|
BlockingCollection<T> | 为实现 IProducerConsumerCollection<T> 的线程安全集合提供阻塞和限制功能。 | |
ConcurrentBag<T> | 表示对象的线程安全的无序集合。 | |
ConcurrentDictionary<TKey, TValue> | 表示可由多个线程同时访问的键值对的线程安全集合。 | |
ConcurrentQueue<T> | 表示线程安全的先进先出 (FIFO) 集合。 | |
ConcurrentStack<T> | 表示线程安全的后进先出 (LIFO) 集合。 | |
OrderablePartitioner<TSource> | 表示将一个可排序数据源拆分成多个分区的特定方式。 | |
Partitioner | 提供针对数组、列表和可枚举项的常见分区策略。 | |
Partitioner<TSource> | 表示将一个数据源拆分成多个分区的特定方式。 |
其中,经常用到是ConcurrentBag<T>、ConcurrentQueue<T>、ConcurrentDictionary<TKey, TValue>。以ConcurrentBag<T>为例。实现上面的过程。首先用它的优点就是不需要再额外写MutilThreadOperClass这个类了。也可以理解为微软用ConcurrentBag<T>代替了自己写的这个安全类。其次就是线程安全,可以*访问集合中的要素了。代码如下:
private void btRun_Click(object sender, EventArgs e)
{
System.Collections.Concurrent.ConcurrentBag<OperPicClass> tConcurrentBag = new System.Collections.Concurrent.ConcurrentBag<OperPicClass>();
for (int i = 0; i < 10000; i++)
{
tConcurrentBag.Add(new OperPicClass() { InFileName = "文件路径" + i, OutFileName = "输出路径" });
}
Thread[] tThread = new Thread[8];
for (int i = 0; i < tThread.Length; i++)
{
tThread[i] = new Thread(() =>
{
OperPicClass d;
while (tConcurrentBag.TryTake(out d))//TryTake方法与TryPeek方法的区别在于是否移除元素。
{
Thread.Sleep(10);
d.Exc();
}
});
tThread[i].IsBackground = true;
tThread[i].Name = "我是线程" + i;
tThread[i].Start();
}
}