首页 新闻 会员 周边 捐助

C#多线程问题?

0
悬赏园豆:10 [已解决问题] 解决于 2013-11-20 21:26

我想实现一个批量下载的任务:

比如有最多有5个任务,每个任务有3(可调)个线程各自进行下载和保存,类似迅雷等软件, 此功能已实现。

但现有一个问题,网络请求是最耗资源的,本地文件存储相对快一些(如果下载的速度比操作本地I/O还快,我想就不叫下载了)。我打算给每个任务再多开一个线程来专门做文件存储的任务,在下载线程收到下载数据后交给保存线程,下载线程立即去请求下一个数据块。想知道这三个线程如何安全地把数据交给本地给I/O线程,有没有成熟的模型?

使用线程池和自己控制线程皆可,我都想知道?

空明流光的主页 空明流光 | 初学一级 | 园豆:111
提问于:2013-11-17 11:21
< >
分享
最佳答案
0

对于IO密集型操作,你应该使用 APM 模型,也就是利用 Socket,File 的异步版本来编写程序,遇到在异步回调中需要长时间执行的代码,可以将其放置到线程池中去执行.

收获园豆:5
Launcher | 高人七级 |园豆:45050 | 2013-11-17 12:51

线程池中如何控制每个线程的暂停和继续,以及如何控制对同一个文件进行并行写入,我不会这个,所以暂时用不了线程池。

空明流光 | 园豆:111 (初学一级) | 2013-11-17 16:53

@沧海一杰: 在I/O密集型的应用程序中,应该采用数据流,而非控制流,通过并发数据结构来同步.因为I/O总是很缓慢.

Launcher | 园豆:45050 (高人七级) | 2013-11-18 09:19

@Launcher: 那么想像迅雷一样,十个任务,每个任务三个线程同时下载,如何停止其中中的某几个任务呢? 采用数据流是在哪一步来停止呢?

空明流光 | 园豆:111 (初学一级) | 2013-11-18 10:40

@沧海一杰: 我现在在停止是给每个任务里放一个全局变量,它们下载过程中会不断判断这个标志,如果为ture就退出,我在stop方法里,

ExitFlag =true;

while(true)

{

     var flag =true;

  foreach(var t in theads)

      if(t.IsAlive)

         flag =false;

     if(flag)

    break;
}

Debug.Print("Stopped");

空明流光 | 园豆:111 (初学一级) | 2013-11-18 10:42

@沧海一杰: 你可以看一下这片文章:http://www.csharpwin.com/dotnetspace/6462r8404.shtml

然后你可以通过给 WebReqState 添加一个 bool isCancelled 成员变量和一个 Cancel(当然通常我们不会这么做,会设计一个 CancellationToken 类) 方法来取消任务.在每次 BeginXXX 之前判断一次 isCancelled 来决定是否继续.

因为你的程序主要是I/O,线程会经常性的被阻塞用于等待 I/O(可以断定,在你写的代码中,被阻塞的线程在I/O返回前是无法用于执行其它代码);而使用 APM 时,线程会立即返回,同时允许再次被调度用于执行其它代码,这样你的程序在I/O请求较多时,就不会创建大量的线程去等待I/O完成。

Launcher | 园豆:45050 (高人七级) | 2013-11-18 11:12

@Launcher: 这个IO其实倒不是最大的瓶颈,最大的瓶颈是网络延迟。上一个数据块和下一个数据块会有阻塞,所以我用了异步,然后轮询,在轮询期间我完成了同步IO保存,性能还是可以的。

空明流光 | 园豆:111 (初学一级) | 2013-11-20 21:25

@沧海一杰: 其实吧这些知识你可以找本《操作系统》的书一看就知道了,但是你好像不喜欢看书,所以我这里就简单说下。I/O(Input/Output),输入/输出,指代的是在计算机和外部环境之间移动数据。外部环境由各种外部设备组成,包括辅助存储设备(如硬盘)、通信设备和终端。你这里的“网络延迟”,实际上就是我说的“I/O阻塞”的一种具体表现。以后吧,如果你再碰到计算机术语,如果没有系统的学习过计算机理论知识,那么最好还是先了解下。

Launcher | 园豆:45050 (高人七级) | 2013-11-21 09:24

@Launcher: 是啊,好长时间没看书了。多谢!

空明流光 | 园豆:111 (初学一级) | 2013-11-21 10:14
其他回答(2)
0

保存本地个人觉得不要每个下载线程都开一个本地IO线程。5个任务一个本地IO线程应该就行了,除非你下载速度非常惊人。

像迅雷这类软件都有个缓存设置

用一个文件缓存队列,和五个下载线程互锁,每当一个下载线程的缓存量达到设定值的时候缓存队列加锁,把缓存复制到缓存队列,然后释放锁,下载线程继续下载。一个本地IO线程锁定缓存队列,拷贝到新队列(为了防止写入磁盘时持续锁定原队列阻碍下载),清空原队列,释放原队列的锁。本地IO线程从新队列逐个写入磁盘。完了继续以上步骤。我自己一个项目实现的方式,仅供参考

收获园豆:2
大芝麻 | 园豆:4 (初学一级) | 2013-11-17 12:32
0

搞一个List做Buffer

搞一个ManualResetEvent作通知

然后网络线程循环中

var data = download(from, to);

lock(buffer){

  buffer.Add(data);

  event.Set();

在IO线程循环中

event.WaitOne();

List copy = new List();

lock(buffer) {

copy.AddRange(buffer);

buffer.Clear();

event.Reset();

}

foreach(var data in copy) {

save(data);

}

 

大致是这么个意思吧 

收获园豆:3
Todd Pointer | 园豆:379 (菜鸟二级) | 2013-11-17 19:11
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册