多线程断点下载开发总结(二)- 多线程写文件

上篇文章提到了向服务器请求部分数据,已达到多线程下载的目的。

这里我们看看如何实现多线程写入文件。先看示例代码:

String url="";//待下载文件网络地址
String path="";//待下载文件本地地址
int startpos,endpos;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
//request.Timeout = Timeout.Infinite;
request.Timeout = 30000;
request.AddRange(startpos,endpos); //采用 http 协议的 Range 头,下载部分数据
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
	using (Stream sReader = response.GetResponseStream())
	{
		using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write))
		{// 打开下载文件,共享写,注意每个线程写的时候从规划好的位置开始,不然会覆盖
			while (len > 0)
			{
				byte[] buffer = new byte[10240];
				int read = sReader.Read(buffer, 0, buffer.Length);
				if (read <= 0)
				{
					break;
				}

				fs.Seek(startpos, SeekOrigin.Begin);//开始写的位置
				fs.Write(buffer, 0, read);
				fs.Flush();
				UpdateStartPos(id, read);//更新待下载文件已下载数据量,id为当前下载文件标志
				len -= read;

			}
		}
	}
}
这里我们看看这几个点:

1.request.AddRange(start,end)。添加head头,请求部分数据。

2.FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write))。打开文件流,共享写,实现多线程写入文件。但是要注意,多线程写入的时候,规划好每个线程写入的其实位置和request添加Range头的位置对应,避免写入文件覆盖数据。fs.Seek(start,SeekOrigin.Begin)

3.那么如何知道这个起始位置呢?

迅雷之前的版本在下载的时候会创建一个下载文件对应的cfg文件,这个cfg文件就是下载文件下载过程中的状态文件,例如当前下载线程数,每个线程起始位置,当前每个线程下载量和总下载量等。下载的同时会实时写入这个配置文件,更新下载状态。在下次启动时,加载这个cfg文件达到上次下载的结束状态,以便开始下载。

这里我们为了方便写入文件同时更新下载进度,我们以10k为单位下载数据写入文件,同时写入配置文件。注意代码中:UpdateStartPos(id, read);//更新待下载文件已下载数据量,id为当前下载文件标志。

这样就实现了多线程写入,断点功能。

这里注意,UpdateStartPos(id, read) 方法,在更新配置文件的时候会有cfg文件锁定的问题。

4. ReaderWriterLock 锁

更新cfg配置文件的时候采用读写锁。在.Net Framework 4 中建议采用ReaderWriterLockSlim。

对于所有新的开发建议使用 ReaderWriterLockSlim。 ReaderWriterLockSlim 类似于 ReaderWriterLock,但简化了递归规则以及升级和降级锁定状态的规则。 ReaderWriterLockSlim 可避免多种潜在的死锁情况。 此外,ReaderWriterLockSlim 的性能明显优于 ReaderWriterLock。
private ReaderWriterLockSlim  _mutex = new ReaderWriterLockSlim();
public readonly int ReaderWriterLockTimeout = 30000;
public void UpdateStartPos(int taskid, int increment)
{
	if(_mutex.TryEnterWriteLock(ReaderWriterLockTimeout)){
		try
		{
			// 更新配置文件,可以是xml txt 甚至数据库,但尽可能是个短时间操作
		}
		finally
		{
			_mutex.ExitWriteLock();
		}
	}
	
}




你可能感兴趣的:(多线程,C#,断点下载,读写锁)