Starting with version 6.1, it is possible to enable the use of multiple threads when zipping with the Deflate or Deflate64 compression methods.
Multiple threads must be explicitly enabled. Support is not active by default because of the large resources used. By default, the number of threads used will be the number of processors on the machine. A maximum of 32 threads can be specified.
Multi-threading works by zipping multiple different files at the same time. The zipping order is not guaranteed to be maintained. The engine does not split single files into different threads. The threads are assigned on a per-file basis. This keeps the compression ratio as high as it can be.
The Xceed.Zip.ZipMultiThreadManager class activates and controls multi threading. Once an instance of the class is assigned to the ZipArchive.MultiThreadManager property, multi threading can be used for zipping. Setting the property to null disables the use of multi threading for the next operations.
The ZipMultiThreadManager object is by default passive. It performs its duties of assigning data to be zipped to running threads and writing zip headers to finished items only when ZipArchive is running. In most scenarios, this is sufficient as shown in the typical usage example below. However, in the scenario where files or folders will be queued for zipping and the application will not call ZipArchive to wait for the operation to complete or add new items for zipping for a significant amount of time, the ZipMultiThreadManager has an option to make itself run automatically in the background. It is not enabled by default.
Multi-threading has limitations. It is only active when Deflate or Deflate64 is used. Each thread uses, by default, 32MB of memory for buffering.
For small zipping operations, where there are less files to zip than the number of threads, there will be no performance gain. The best performance gains come from scenarios where a large number of small to medium-sized files are zipped as this saturates the CPU cores. Because the threads are assigned at the file level, a scenario where a single very large file is zipped would not see a performance gain.
The ZipMultiThreadManager constructor takes a Boolean parameter that specifies if the manager should run itself periodically (currently every 500ms). By default, the value if false. It is not currently possible to change the value after the object has been created.
It is generally not necessary to enable background running of the manager as typical code patterns will make the manager run often enough. There is also a slight performance penalty if the option is enable without a real need. However, in scenarios where an application will queue items for zipping and then start a lengthy unrelated task, the manager will not have an opportunity to run. Then enabling background running becomes desirable and will dramatically improve performance.
In this example, the multi-thread manager object is used by multiple zip archives sequentially. This means that the multiple threads are only created once, saving some contiguous memory.
Sharing the manager object between archives does not mean that they will share data. The manager is just the tool that owns the background threads and coordinates the compression workloads assigned to it.
The ZipMultiThreadManager class is not meant to be used by multiple threads at the same time. Such usage will result in undefined behavior.
The example also shows you can choose which zipping operation will use threads.
Events from the FileSystemEvents class are triggered when a zipping operation is performed under the ZipMultiThreadManager similar to single-threaded operation, with some differences.
The ItemProgression event is triggered when an item is about to start to be compressed by the ZipMultiThreadManager.
The ItemCompletion event is triggered when an item has completed the zipping process. You will get the event even if there is an exception and the chosen action is to 'ignore' the item and proceed with the next one.
The ItemException event will be triggered if an exception occurs during processing. Even with the ZipMultiThreadManager, it is possible to Abort, Retry or Ignore the operation.
The ByteProgression event is triggered as needed during zipping. An application can depend on getting a consistent trigger at 0% completion and at 100% completion.
The ItemProgression, ItemCompletion and ItemException events will only be triggered from the main thread. Never from background threads. So the event handler code for these events does not need to be thread-safe.
The ByteProgression event will be triggered from both background threads (during compression) and the main thread (the first 0% completion and last 100% completion call). So the code in the event handler might be executed by several different threads at the same time for different items. Therefore, the ByteProgression event handler code might need to be thread-safe.
The progression events will correctly compute the progression numbers and percentages for both the 'current file' and 'all files. This includes the ByteProgression event. The 'Processed' value for the 'all files' tracking remains coherent even when multiple threads report byte progress at the same time.
If the value of the UserData property is changed in one of the event handlers, the value will be propagated to the other event handlers regardless of the item, just like the single-threaded execution. Except for the ByteProgression event. For this event, each item has its own UserData value. It is initialized with the current value of the session's UserData just after the ItemProgression event is triggered for the item. After that, the UserData value included in the ByteProgression event handler for that item is independent. It may be changed in the event handler and it will be remembered, but only for the ByteProgression event for the item. It will not propagate to the other events.
Because of this, it is safe to change the value of the UserData property in the ByteProgression event handler without using thread synchronization (like a lock() block for example).