300分!!!!!多个线程同时写一个文件??(300分)

  • 主题发起人 主题发起人 Writer
  • 开始时间 开始时间
W

Writer

Unregistered / Unconfirmed
GUEST, unregistred user!
就像网络快车一样,如何用多个线程,从同一的数据源取得数据,并同时写入同一文件,
而且写入多少,文件就是多大,不是写完后才是最后的大小。
只要思路就可以了,论者有分!
 
我是这样想的,FlashGet是这样做的:
首先先在盘上创建一个文件,文件的大小事先就确定好了;
然后在各个线程中分别读取数据,
根据确定好的块的位置写入文件中。
你要的写入多少就是多少恐怕有点困难。
 
setfileend 函数。
 
写的问题不大,可是你如何知道读进来的数据是不重复的呢,所以读取
的位置和长度应该预先知道,这样,读最后一块的线程把剩下的数据全
读出来读多少是多少。
 
《delphi directx 图形与游戏设计》
[美] john ayres 著 余泳等 译 电子科技大学出版社出版 45元.
这本书还不错,从最基本的开始讲jedi翻译的DirectxAPI,不过讲的是directx5,directdraw头文件的名字(ddraw)和现在不一样(directdraw).
这儿有该书的电子版(英文,不全):
http://go.163.com/progame/mytest.html
这本书带的光盘很有意思,居然有delphi1,delphi4,delphi5trail,另外还有一些例子,值得大家看一看!
在win2000上装了个delphi1,居然可以很好的工作!
 
我做过类似的程序,我讲讲flashget的思路吧。
现假设一个文件90m,用3个线程来下载。
flashget是这样的:
它把文件分为3部分来下载,每个部分都是30m,
启动的第一个线程,如果连接服务器成功,那么开始下载,
创建文件的时候,把文件指针指向0(即文件开头),然后开始写文件。
启动的另一个线程(线程启动总是有先后的),如果连接服务器成功,那么开始下载,
这时候,不管第一个线程写了多少,都把文件大小扩展为30m(扩展文件不会损坏已写数据),
文件指针指向30m的地方开始下载。
第三个线程和第二个线程类似(文件指针指向60m)。
因为线程之间写的是文件的不同位置,所以不会产生冲突,所以不用互斥。
上面就是flashget写文件的思路了
对于读文件的思路和写文件的思路是一致的,还更加简单一些,这里就不多说了。
所以你说的“写入多少,文件就是多大”其实这种说法是不严谨的,
比如第一部分没有下载完的时候,第二个线程启动了,那么它把文件扩展为30m,
而实际上,下载的数据并没有这么多。
至以扩展文件的函数在Delphi里是seek();
说的很具体了,给分吧。
 
记得CuteFTP Pro支持多线程下载的(最多4线程),而且其是采用了逐步写入文件的方式,即下载多少写多少,但我不大用它的多线程下载,故不知它在多线程下载时是否也是逐步写入。
 
用一个TFileStream打开文件,多线程同时写没有问题,如果不放心可以加信号灯,
可参考 NetMasters 的 FastNet控件组中的 TNMFifoBuffer 的源程序。
TCP/IP是慢协议,FlashGet、CuteFTP几个线程同时写文件几乎不可能冲突。
另外我的断点续传软件快做完了,近期将在主页提供下载。
 
你可以用多个线程来组织数据,然后用一个线程来写入文件,
想像一个小卖部,每个买东西的人都用一个线程来表示,
卖东西的那个人也用一个线程来表示,卖东西的一次只能完成一笔交易……
对操作系统来说,多个线程共用一个文件句柄读写文件是不安全的,要么你就需要
很复杂的PV机制对它们进行管理,而这样又可能引起线程之间的写等待,降低工作
效率。因为一个线程的写操作未完成时,另一个线程不能进行写操作,否则将引起
文件指针的摆动而导致写入数据紊乱。
而用线程分工的办法去做,就相对简单些,有专门的线程处理文件,其它组织数据
的线程就不必等待其数据写入文件后才能准备下一批数据。
思路:
1、设计一个记录类型TWriteRec,包括三个字段:缓冲区地址、文件位置、写入大小。
2、定义一个写操作队列(TList 或其它)TWriteList,用于保存写文件的数据记录TWriteRec。
3、数据组织线程(多个)TGetThread,
a.建立一个TWriteRec记录,申请缓冲区,取得数据,向TWriteList加入该记录。
b.重复a.
4、写文件线程(一个)TWriteThread,
c.从TWriteList取出一条TWriteRec记录,写入文件,释放缓冲区。释放该记录。
d.重复c.
5、需要注意的是,对队列TWriteList的操作需要做PV保护。

 
补充一下:
写操作基本上按以下方式:
fileseek(handle,position,0);
filewrite(handle,buffer,wsize);
因为fileseek可以移动到超过文件当前大小的位置,
所以可以实现FlashGet一样的写多少变多大的效果。
 
我所说的seek其实是调用你所说的
fileseek(handle,position,0);
这个函数。
>>FlashGet一样的写多少变多大的效果
它的算法是上面我所说的,但不是完全的“写多少变多大”。
 
to Campo:
>>它的算法是上面我所说的,但不是完全的“写多少变多大”。
呵呵,我想Writer的意思也不是想要完全的"写多少变多大",我的意思也不是:)
准确地说应该是"写到什么位置变多大",
比如:
fileseek(handle,4096,0);
filewrite(handle,'abc',3);
写入3字节,文件大小4099.
 
楼上的各位写得够好了!
我同意文件大小我想一定是一开始已经定了,各线程是写文件的不同位置!
 
看到各位的回复很感动呀,放了这么多天才几个高手回答。
看了上面的回复,我还有下面的问题:
流不FREE前之前是0字节的呀,怎么办?不是每写一次FREE一次吧。
大家继续论讨吧。
 
Campo说得很好了。
 
FileStream 打开文件后,文件就不再允许其他程序写了(读也容易出错误)。
没有必要频繁释放流。完全可以通过 Stream.size 取文件大小的。
 
>>FileStream 打开文件后,文件就不再允许其他程序写了(读也容易出错误)。
>>没有必要频繁释放流。完全可以通过 Stream.size 取文件大小的。
我当然知Stream.size可以得到文件大小,但如果不FREE文件在WINDOWS下还是0字节的,
如果当机了什么也没有了。
 
用 FlushFileBuffers(FStream.Handle);
将内存中的数据写入磁盘。
文件的大小对系统就可视了。
 
Remarks
The WriteFile and WriteFileEx functions typically write data to an internal buffer
that the operating system writes to disk on a regular basis. The FlushFileBuffers
function writes all of the buffered information for the specified file to disk.
 
多谢OopsWare、Campo、skyweb几位大侠!
 
后退
顶部