原文链接:Multithreading Backup Utility

多线程文件备份(VB.NET版)_多线程

   
简介

多线程迟早是我们要面对的一个东西,本文向你展示了一个简单的使用两个线程来拷贝文件的应用程序

Backup将一个目录中文件拷贝到另一个目录中,它使用了一个线程来显示正被拷贝的文件名称,另一个线程用来在拷贝的文件的同时统计文件数目和文件夹数目。这就意味着在拷贝可以开始之前不用浪费时间去等待文件数目的统计完成,我们使用了两个线程同时完成拷贝和统计工作。

Backup对于大数量文件的拷贝也是快速和有效率的。它很快是因为当目标文件已经存在并且没有改变过时就不做拷贝工作,因此它对于重复拷贝来说不错,因为它只拷贝新文件或更新过的文件。

这个程序的另一个特点是当在一个特点文件上发生安全性错误或其他类型错误(当然不包括硬件错误),它不会停止拷贝工作,它将会记录下错误信息并继续完成工作。过后你可以去查看日志文件,它会告诉你发生了什么错误。大部分错误都是由于安全配置问题产生的。

背景知识

为什么要使用多线程呢?一个原因可能一个窗口在忙碌时,你想能点击窗口上的一个按钮。另一个原因是多核时代已经来临,硬件和操作系统级别的多任务也存在,线程无可避免,尤其当我们关注性能时。

好的,你已经决定你想在.Net中使用多线程。你可以使用BackgroundWorker, 不过我假定你应该从System.Threading开始,并直接使用Thread类。个人看来,它更容易使用,并且更具灵活性。

那么线程到底是什么呢?它就好比于一个源自于你的主程序的另一个完全分离的程序。一旦线程启动,主程序对线程完全不知道。它们生死自控。要启动一个或两个线程,你可能想知道如何创建线程,如何传递数据,如何从子线程中回调主应用程序以及主应用程序如何知道是哪个子线程回调了它?下面的代码片段会回答这些问题的。

最后一点,作者将一个类实例的方法赋予了一个线程,尽管可以将主程序的方法赋予线程,但作者认为这样做更好。

None.gif
None.gif    
' Declare first thread variable. This will be used to copy the files.
None.gif
    Private CopyThread As Thread '拷贝线程
None.gif

None.gif    
' Delclare second thread variable. This will be used to count the folders and files.
None.gif
Private CountThread As Thread '统计线程
None.gif

统计线程一般在拷贝线程前面完成,除非你拷贝的文件数目很小,那种情况下一切都发生的很快。

另一方面,统计线程结束后,总文件数目会被统计出来,从而用来设置ProgressBar1.Max属性,

下面是启动拷贝线程和统计线程的代码:

ExpandedBlockStart.gifContractedBlock.gif  Private Sub StartCopy_Click()Sub StartCopy_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles StartCopy.Click
InBlock.gif        
'开始拷贝
InBlock.gif
        ' Validate from and to folders
InBlock.gif
        If Not SetRootPath Then Exit Sub
InBlock.gif        
If Not CheckFromPath() Then Exit Sub
InBlock.gif        
If Not CheckToPath() Then Exit Sub
InBlock.gif
InBlock.gif        
' Create an instance of the copy class that will be assigned to the first thread.
InBlock.gif
        Dim FileCopy As New CopyClass(Me)
InBlock.gif
InBlock.gif        
' Set required properties
InBlock.gif
        FileCopy.FromPath = FromPathTextbox.Text
InBlock.gif        
FileCopy.ToPath = ToPathTextbox.Text & _rootDir
InBlock.gif        
FileCopy.StartDateTime = DateTime.Now
InBlock.gif
InBlock.gif        
'Save log file name
InBlock.gif
        _logFile = FileCopy.LogFileName
InBlock.gif
InBlock.gif        
' Create the thread and assign the class instance and method to execute 
InBlock.gif
        ' (CopyFiles in this case) when the thread is started.
InBlock.gif
        CopyThread = New Thread(AddressOf FileCopy.CopyFiles) '拷贝线程
InBlock.gif

InBlock.gif        
' Start the first thread to copy the files.
InBlock.gif
        CopyThread.Name = "Copy"
InBlock.gif        CopyThread.IsBackground 
= True
InBlock.gif        CopyThread.Start()
InBlock.gif
InBlock.gif        
' Create another instance of the copy class that will be assigned to the second thread.
InBlock.gif
        Dim FileFolderCount As New CopyClass(Me)
InBlock.gif
InBlock.gif        
' Set required properties
InBlock.gif
        FileFolderCount.FromPath = FromPathTextbox.Text
InBlock.gif        FileFolderCount.ToPath 
= ToPathTextbox.Text
InBlock.gif
InBlock.gif        
' Create the thread and assign the class instance and method to execute 
InBlock.gif
        ' (CopyFiles in this case) when the thread is started.
InBlock.gif
        CountThread = New Thread(AddressOf FileFolderCount.GetCountData) '计数线程
InBlock.gif

InBlock.gif        
' Start the second thread to count folders and files while the copy is running at the same time.
InBlock.gif
        CountThread.Name = "Count"
InBlock.gif        CountThread.IsBackground 
= True
InBlock.gif        CountThread.Start()
InBlock.gif
InBlock.gif        
' Reset form controls
InBlock.gif
        StartCopy.Enabled = False
InBlock.gif        Panel1.Enabled 
= False
InBlock.gif        StopCopy.Enabled 
= True
InBlock.gif
ExpandedBlockEnd.gif    
End Sub

None.gif

下面是终止两个线程的代码:

ExpandedBlockStart.gifContractedBlock.gif    Private Sub StopCopy_Click()Sub StopCopy_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles StopCopy.Click
InBlock.gif        
'终止线程
InBlock.gif
        If CopyThread.IsAlive Then CopyThread.Abort()
InBlock.gif        
If CountThread.IsAlive Then CountThread.Abort()
ExpandedBlockEnd.gif    
End Sub

None.gif

主界面的两个delegate方法,用来响应子线程的回调,刷新主界面

ExpandedBlockStart.gifContractedBlock.gif    Public Sub CopyThreadMessage()Sub CopyThreadMessage(ByVal ThreadName As StringByVal CopiedFiles As LongByVal Message As String)
InBlock.gif
InBlock.gif        
' If finished copying
InBlock.gif
        If Message = "END" Then
InBlock.gif            lblStatus.Text 
= "Status: Copy Finsihed. Copied " + _totalFiles.ToString + " files in " + _totalFolders.ToString + " folders."
InBlock.gif            txtFile.Text 
= "Copy completed successfully."
InBlock.gif            ProgressBar1.Value 
= ProgressBar1.Maximum
InBlock.gif            CopyThread.Abort()
InBlock.gif            CountThread.Abort()
InBlock.gif            
Exit Sub
InBlock.gif        
End If
InBlock.gif
InBlock.gif        
' Show current file
InBlock.gif
        txtFile.Text = "Copying: " & Message
InBlock.gif
InBlock.gif        
' Update progressbar
InBlock.gif
        If ProgressBar1.Maximum <> 0 Then ProgressBar1.Value = _totalFiles - (_totalFiles - CopiedFiles)
InBlock.gif
InBlock.gif        
' Update status (TotalFiles not zero means counting has finished)
InBlock.gif
        If _totalFiles <> 0 Then
InBlock.gif            lblStatus.Text 
= "Status: Copying. There are " + _totalFiles.ToString + " files in " + _totalFolders.ToString + " folders. Files copied so far " & CopiedFiles & "."
InBlock.gif        
End If
InBlock.gif
InBlock.gif        
' Save for CountThreadMessage()
InBlock.gif
        _copiedFiles = CopiedFiles
InBlock.gif
ExpandedBlockEnd.gif    
End Sub

ExpandedBlockStart.gifContractedBlock.gif    
Public Sub CountThreadMessage()Sub CountThreadMessage(ByVal ThreadName As StringByVal Files As LongByVal TotalFiles As LongByVal Folders As LongByVal Message As String)
InBlock.gif
InBlock.gif        
' Display current count
InBlock.gif
        lblStatus.Text = "Status: Copying and Counting. So far there are " + Files.ToString + " files in " + Folders.ToString + " folders."
InBlock.gif
InBlock.gif        
' Save totals when finished counting for CopyThreadMessage()
InBlock.gif
        If Message = "END" Then
InBlock.gif            _totalFiles 
= TotalFiles
InBlock.gif            _totalFolders 
= Folders
InBlock.gif            lblStatus.Text 
= "Status: Copying. There are " + _totalFiles.ToString + " files in " + _totalFolders.ToString + " folders. Files copied so far " & _copiedFiles & "."
InBlock.gif            ProgressBar1.Maximum 
= _totalFiles
InBlock.gif            ProgressBar1.Value 
= _totalFiles - (_totalFiles - _copiedFiles)
InBlock.gif        
End If
InBlock.gif
ExpandedBlockEnd.gif    
End Sub

None.gif

负责拷贝和统计的类:

None.gifImports System.Threading
None.gif
Imports System.IO
ExpandedBlockStart.gifContractedBlock.gif
Public Class CopyClassClass CopyClass
InBlock.gif    
'This will hold the reference to the client form
InBlock.gif
    Private _clientApp As Form
InBlock.gif
InBlock.gif    
'Create a delegate method that will map to the CopyThreadMessage method of the client app
ExpandedSubBlockStart.gifContractedSubBlock.gif
    Private Delegate Sub CallClientCopy()Sub CallClientCopy(ByVal ThreadName As StringByVal FilesRemaining As LongByVal Message As String)
InBlock.gif
InBlock.gif   
'Create a delegate method that will map to the CountThreadMessage method of the client app
ExpandedSubBlockStart.gifContractedSubBlock.gif
    Private Delegate Sub CallClientCount()Sub CallClientCount(ByVal ThreadName As StringByVal TotalFiles As LongByVal TotalFolders As LongByVal Files As LongByVal Message As String)
InBlock.gif
InBlock.gif    
'Create an object for each deletegate
InBlock.gif
    Private _callClientCopy As CallClientCopy
InBlock.gif    
Private _callClientCount As CallClientCount
InBlock.gif
InBlock.gif    
' Property variables
InBlock.gif
    Private _firstTime As Boolean
InBlock.gif    
Private _fromPath As String
InBlock.gif    
Private _toPath As String
InBlock.gif    
Private _directories As Long
InBlock.gif    
Private _files As Long
InBlock.gif    
Private _copiedFiles As Long
InBlock.gif    
Private _totalFiles As Long
InBlock.gif    
Private _fileName As String
InBlock.gif    
Private _logFile As StreamWriter
InBlock.gif    
Private _startDateTime As Date
InBlock.gif    
Private _logFileName As String
InBlock.gif
InBlock.gif    
' Constants
InBlock.gif
    Private Const LOG_FILE As String = "BackupLog.txt"
InBlock.gif    
Private Const ERR_MSG As String = "Error accessing file: "
ExpandedSubBlockStart.gifContractedSubBlock.gif    
Public Sub New()Sub New(ByRef ClientApp As Backup)
InBlock.gif
InBlock.gif        
' Save the reference to the client app
InBlock.gif
        _clientApp = ClientApp
InBlock.gif
InBlock.gif        
' Assign delegate objects
InBlock.gif
        _callClientCopy = AddressOf ClientApp.CopyThreadMessage
InBlock.gif        _callClientCount 
= AddressOf ClientApp.CountThreadMessage
InBlock.gif
ExpandedSubBlockEnd.gif    
End Sub

ExpandedSubBlockStart.gifContractedSubBlock.gif    
Public Sub CopyFiles()Sub CopyFiles()
InBlock.gif        
'Do the work of the first thread here
InBlock.gif

InBlock.gif        
' Give this thread a name
InBlock.gif
        If Thread.CurrentThread.Name = Nothing Then Thread.CurrentThread.Name = "Copy"
InBlock.gif
InBlock.gif        
' Create a new DirectoryInfo object for from path.
InBlock.gif
        Dim dir As New DirectoryInfo(FromPath)
InBlock.gif
InBlock.gif        
' Call the GetFileSystemInfos method.
InBlock.gif
        Dim FSinfo As FileSystemInfo() = dir.GetFileSystemInfos
InBlock.gif
InBlock.gif        
' Open log file
InBlock.gif
        OpenLog()
InBlock.gif
InBlock.gif        
'Copy one file at a time looping until all files are copied
InBlock.gif
        ReallyCopyFiles(FSinfo)
InBlock.gif
InBlock.gif        WriteLog(
"Copy completed successfully.")
InBlock.gif
InBlock.gif        
'Call client one last time to signal end of copy
InBlock.gif
        CallClient(Thread.CurrentThread.Name, _copiedFiles, _totalFiles, _directories, "END")
InBlock.gif
ExpandedSubBlockEnd.gif    
End Sub

ExpandedSubBlockStart.gifContractedSubBlock.gif    
Public Sub GetCountData()Sub GetCountData()
InBlock.gif        
'Do the work of the second thread here
InBlock.gif

InBlock.gif        
' Give this thread a name
InBlock.gif
        If Thread.CurrentThread.Name = Nothing Then Thread.CurrentThread.Name = "Count"
InBlock.gif
InBlock.gif        
' Create a new DirectoryInfo object for from path.
InBlock.gif
        Dim dir As New DirectoryInfo(FromPath)
InBlock.gif
InBlock.gif        
' Call the GetFileSystemInfos method.
InBlock.gif
        Dim FSinfo As FileSystemInfo() = dir.GetFileSystemInfos
InBlock.gif
InBlock.gif        
' Count folder and files
InBlock.gif
        CountFiles(FSinfo)
InBlock.gif
InBlock.gif        
' Save total files count
InBlock.gif
        _totalFiles = _files
InBlock.gif
InBlock.gif        
' Send message to client form
InBlock.gif
        CallClient(Thread.CurrentThread.Name, _files, _totalFiles, _directories, "END")
InBlock.gif
ExpandedSubBlockEnd.gif    
End Sub

ExpandedSubBlockStart.gifContractedSubBlock.gif    
Private Sub ReallyCopyFiles()Sub ReallyCopyFiles(ByVal FSInfo As FileSystemInfo())
InBlock.gif        
' Check the FSInfo parameter.
InBlock.gif
        If FSInfo Is Nothing Then
InBlock.gif            
Throw New ArgumentNullException("FSInfo")
InBlock.gif        
End If
InBlock.gif
InBlock.gif        
' Iterate through each item.
InBlock.gif
        Dim i As FileSystemInfo
InBlock.gif        
For Each i In FSInfo
InBlock.gif
InBlock.gif            
Try
InBlock.gif                
' Check to see if this is a DirectoryInfo object.
InBlock.gif
                If TypeOf i Is DirectoryInfo Then
InBlock.gif                    
' Cast the object to a DirectoryInfo object.
InBlock.gif
                    Dim dInfo As DirectoryInfo = CType(i, DirectoryInfo)
InBlock.gif
InBlock.gif                    
' Iterate (recurse) through all sub-directories.
InBlock.gif
                    ReallyCopyFiles(dInfo.GetFileSystemInfos())
InBlock.gif                    
' Check to see if this is a FileInfo object.
InBlock.gif
                ElseIf TypeOf i Is FileInfo Then
InBlock.gif                    
'save the full path and file name
InBlock.gif
                    _fileName = i.FullName
InBlock.gif
InBlock.gif                    
'Get the copy path name only
InBlock.gif
                    Dim copypath As String = ToPath & Mid(_fileName, Len(FromPath) + 1Len(_fileName) - Len(FromPath) - Len(i.Name))
InBlock.gif
InBlock.gif                    
'Create copy path if it does not exist
InBlock.gif
                    If Not Directory.Exists(copypath) Then
InBlock.gif                        Directory.CreateDirectory(copypath)
InBlock.gif                    
End If
InBlock.gif
InBlock.gif                    
' Get the to path and filename
InBlock.gif
                    Dim tofile As String = ToPath & Mid(_fileName, Len(FromPath) + 1)
InBlock.gif
InBlock.gif                    
' Update status info on client
InBlock.gif
                    Dim fi As New FileInfo(_fileName)
InBlock.gif                    
Dim Message As String = _fileName & " is " & Decimal.Round(CDec(fi.Length / 1048576), 2& "MB in length."
InBlock.gif                    CallClient(Thread.CurrentThread.Name, _copiedFiles, _totalFiles, _directories, Message)
InBlock.gif
InBlock.gif                    
' if file exists check if file has been updated since last copy
InBlock.gif
                    Dim OkayToCopy As Boolean = True
InBlock.gif                    
If File.Exists(tofile) Then
InBlock.gif                        
If File.GetLastWriteTime(_fileName) = File.GetLastWriteTime(tofile) Then
InBlock.gif                            OkayToCopy 
= False
InBlock.gif                        
End If
InBlock.gif                    
End If
InBlock.gif
InBlock.gif                    
' Copy file with overwrite
InBlock.gif
                    If OkayToCopy Then File.Copy(_fileName, tofile, True)
InBlock.gif
InBlock.gif                    
' Increment copied file count
InBlock.gif
                    _copiedFiles += 1
InBlock.gif
InBlock.gif                
End If
InBlock.gif            
Catch ex As Exception
InBlock.gif                
' Report error but continue processing
InBlock.gif
                WriteLog(ERR_MSG & _fileName & vbCrLf & ex.Message.ToString)
InBlock.gif            
End Try
InBlock.gif
InBlock.gif        
Next i
InBlock.gif
ExpandedSubBlockEnd.gif    
End Sub

ExpandedSubBlockStart.gifContractedSubBlock.gif    
Private Sub CountFiles()Sub CountFiles(ByVal FSInfo As FileSystemInfo())
InBlock.gif        
Static ShowCount As Long = 0
InBlock.gif
InBlock.gif        
' Check the FSInfo parameter.
InBlock.gif
        If FSInfo Is Nothing Then
InBlock.gif            
Throw New ArgumentNullException("FSInfo")
InBlock.gif        
End If
InBlock.gif
InBlock.gif        
' Iterate through each item.
InBlock.gif
        Dim i As FileSystemInfo
InBlock.gif        
For Each i In FSInfo
InBlock.gif
InBlock.gif            
Try
InBlock.gif                
' Check to see if this is a DirectoryInfo object.
InBlock.gif
                If TypeOf i Is DirectoryInfo Then
InBlock.gif                    
' Add one to the directory count.
InBlock.gif
                    _directories += 1
InBlock.gif
InBlock.gif                    
' Cast the object to a DirectoryInfo object.
InBlock.gif
                    Dim dInfo As DirectoryInfo = CType(i, DirectoryInfo)
InBlock.gif
InBlock.gif                    
' Iterate (recurse) through all sub-directories.
InBlock.gif
                    CountFiles(dInfo.GetFileSystemInfos())
InBlock.gif                    
' Check to see if this is a FileInfo object.
InBlock.gif
                ElseIf TypeOf i Is FileInfo Then
InBlock.gif                    
' Add one to the file count.
InBlock.gif
                    _files += 1
InBlock.gif
InBlock.gif                    
'display count for first file in every folder then every 200 - for faster performance
InBlock.gif
                    Select Case ShowCount
InBlock.gif                        
Case 0
InBlock.gif                            
' Display count
InBlock.gif
                            CallClient(Thread.CurrentThread.Name, _files, _totalFiles, _directories, "")
InBlock.gif                        
Case Is >= 200
InBlock.gif                            
' Display count
InBlock.gif
                            CallClient(Thread.CurrentThread.Name, _files, _totalFiles, _directories, "")
InBlock.gif
InBlock.gif                            
'reset so display is every 200 files in folder
InBlock.gif
                            ShowCount = 0
InBlock.gif                    
End Select
InBlock.gif
InBlock.gif                    
'Increment show count
InBlock.gif
                    ShowCount += 1
InBlock.gif
InBlock.gif                
End If
InBlock.gif            
Catch ex As Exception
InBlock.gif                
'Record error then continue (like a resume next)
InBlock.gif
                WriteLog(ERR_MSG & _fileName & vbCrLf & ex.Message.ToString)
InBlock.gif            
End Try
InBlock.gif
InBlock.gif        
Next i
InBlock.gif
ExpandedSubBlockEnd.gif    
End Sub

ExpandedSubBlockStart.gifContractedSubBlock.gif    
Private Sub CallClient()Sub CallClient(ByVal ThreadName As StringByVal Files As LongByVal TotalFiles As LongByVal Directories As LongByVal Message As String)
InBlock.gif
InBlock.gif        
Select Case ThreadName
InBlock.gif            
Case "Copy"
InBlock.gif                
'Call the delegated method
InBlock.gif
                _clientApp.Invoke(_callClientCopy, ThreadName, Files, Message)
InBlock.gif            
Case "Count"
InBlock.gif                
'Call the delegated method
InBlock.gif
                _clientApp.Invoke(_callClientCount, ThreadName, Files, TotalFiles, Directories, Message)
InBlock.gif        
End Select
InBlock.gif
InBlock.gif        
'Let the thread sleep before continuing so the client app will have time to be process (1 millisecond is enough)
InBlock.gif
        Thread.Sleep(0)
ExpandedSubBlockEnd.gif    
End Sub

ExpandedSubBlockStart.gifContractedSubBlock.gif    
Private Sub OpenLog()Sub OpenLog()
InBlock.gif
InBlock.gif        
' Create log file
InBlock.gif
        If Not File.Exists(StartDateTime & "-" & LOG_FILE) Then
InBlock.gif            
Using _logFile As StreamWriter = File.CreateText(LogFileName)
InBlock.gif                _logFile.WriteLine(
"Logfile name is: " & LogFileName)
InBlock.gif                _logFile.WriteLine(
"BACKUP LOG FILE STARTED AT: " & StartDateTime.ToString)
InBlock.gif                _logFile.WriteLine(
"================================================")
InBlock.gif                _logFile.Write(
"Copying FROM: " & _fromPath)
InBlock.gif                _logFile.WriteLine()
InBlock.gif                _logFile.Write(
"Copying TO: " & _toPath)
InBlock.gif                _logFile.WriteLine()
InBlock.gif                _logFile.Close()
InBlock.gif            
End Using
InBlock.gif        
End If
InBlock.gif
ExpandedSubBlockEnd.gif    
End Sub

ExpandedSubBlockStart.gifContractedSubBlock.gif    
Private Sub WriteLog()Sub WriteLog(ByVal Message As String)
InBlock.gif
InBlock.gif        
' Create an instance of StreamWriter to write text to a file.
InBlock.gif
        Using _logFile As StreamWriter = File.AppendText(LogFileName)
InBlock.gif            
' Add some text to the file.
InBlock.gif
            _logFile.WriteLine()
InBlock.gif            _logFile.WriteLine(
"TIME OF LOG ENTRY: " & DateTime.Now)
InBlock.gif            
' Arbitrary objects can also be written to the file.
InBlock.gif
            _logFile.WriteLine(Message)
InBlock.gif            _logFile.Flush()
InBlock.gif            _logFile.Close()
InBlock.gif        
End Using
InBlock.gif
ExpandedSubBlockEnd.gif    
End Sub

ExpandedSubBlockStart.gifContractedSubBlock.gif    
Private Sub CloseLog()Sub CloseLog()
InBlock.gif        
If File.Exists(LogFileName) Then _logFile.Close()
ExpandedSubBlockEnd.gif    
End Sub

ExpandedSubBlockStart.gifContractedSubBlock.gif    
Private Property FirstTime()Property FirstTime() As Boolean
InBlock.gif        
Get
InBlock.gif            
Return _firstTime
InBlock.gif        
End Get
InBlock.gif        
Set(ByVal value As Boolean)
InBlock.gif            _firstTime 
= value
InBlock.gif        
End Set
ExpandedSubBlockEnd.gif    
End Property

ExpandedSubBlockStart.gifContractedSubBlock.gif    
Public Property FromPath()Property FromPath() As String
InBlock.gif        
Get
InBlock.gif            
Return _fromPath
InBlock.gif        
End Get
InBlock.gif        
Set(ByVal value As String)
InBlock.gif            _fromPath 
= value
InBlock.gif        
End Set
ExpandedSubBlockEnd.gif    
End Property

ExpandedSubBlockStart.gifContractedSubBlock.gif    
Public Property ToPath()Property ToPath() As String
InBlock.gif        
Get
InBlock.gif            
Return _toPath
InBlock.gif        
End Get
InBlock.gif        
Set(ByVal value As String)
InBlock.gif            _toPath 
= value
InBlock.gif        
End Set
ExpandedSubBlockEnd.gif    
End Property

ExpandedSubBlockStart.gifContractedSubBlock.gif    
Public Property StartDateTime()Property StartDateTime() As Date
InBlock.gif        
Get
InBlock.gif            
Return _startDateTime
InBlock.gif        
End Get
InBlock.gif        
Set(ByVal value As Date)
InBlock.gif            _startDateTime 
= value
InBlock.gif        
End Set
ExpandedSubBlockEnd.gif    
End Property

ExpandedSubBlockStart.gifContractedSubBlock.gif    
Public ReadOnly Property LogFileName()Property LogFileName() As String
InBlock.gif        
Get
InBlock.gif            
Return Format(StartDateTime, "yyMMdd-hhmmss"& "-" & LOG_FILE
InBlock.gif        
End Get
ExpandedSubBlockEnd.gif    
End Property

ExpandedSubBlockStart.gifContractedSubBlock.gif    
Public ReadOnly Property Directories()Property Directories() As Long
InBlock.gif        
Get
InBlock.gif            
Return _directories
InBlock.gif        
End Get
ExpandedSubBlockEnd.gif    
End Property

ExpandedSubBlockStart.gifContractedSubBlock.gif    
Public ReadOnly Property Files()Property Files() As Long
InBlock.gif        
Get
InBlock.gif            
Return _files
InBlock.gif        
End Get
ExpandedSubBlockEnd.gif    
End Property

ExpandedSubBlockStart.gifContractedSubBlock.gif    
Public ReadOnly Property TotalFiles()Property TotalFiles() As Long
InBlock.gif        
Get
InBlock.gif            
Return _totalFiles
InBlock.gif        
End Get
ExpandedSubBlockEnd.gif    
End Property

InBlock.gif
ExpandedBlockEnd.gif
End Class

None.gif

作者的程序中还实现了一个比较有意思的特点,用户可以通过指定命令行参数进入后台命令行模式,而非界面模式,

Last Words

      感谢作者的这篇文章,让我能参照他的思路用VC++实现了一个类似的多线程的文件备份工具。

 

Reference:

 

原文链接:Multithreading Backup Utility