C#: Работа с потоками (2).
Краткая шпаргалка — часть 2.
Использование TPL — Task parallel library — System.Threading.Tasks позволяет делать параллельный код без необходимости напрямую иметь дело с потоками или пулами потоков. Ключевым классом в TPL является System.Threading.Tasks.Parallel позволяющий делать итерацию по объектам IEnumerable в параллельном режиме. Контейнер может быть массивом, коллекцией или результатом запроса LINQ. Делегаты System.Func и System.Action понадобится чтобы задать целевой метод, который будет вызываться для обработки данных.
private void button1_Click(object sender, EventArgs e) { ProcessFiles(); } private void ProcessFiles() { // Загрузить все файлы *.jpg и создать новую папку для модифицированных данных. string[] files = Directory.GetFiles(@"D:\src1", "*.jpg",SearchOption.AllDirectories); string newDir = @"D:\ModifiedPictures\src1";Directory.CreateDirectory(newDir); //обработка в параллелном режиме, в качестве делегата используем лямбда-выражение //Можно Parallel.For Parallel.ForEach(files, currentFile => { string fileName = Path.GetFileName(currentFile); using (Bitmap bitmap = new Bitmap(currentFile)) { bitmap.RotateFlip(RotateFlipType.Rotate90FlipNone); bitmap.Save(Path.Combine(newDir, fileName)); //К родительской форме из потока можно добраться только из метода Incvoke //обеспечивающий потокобезопасность //Вызвать Invoke на объекте Form, чтобы позволить вторичным потокам // получать доступ к элементам управления в безопасной к потокам манере. //WPF для той же цели должен использоваться вызов this.Dispatcher. Invoke() this.Invoke ( //используем анонимный делегат (Action)delegate { this.Text = string.Format("Processing {0} on thread {1}", fileName, Thread.CurrentThread.ManagedThreadId); } ); } } ); }
Есть один недостаток: при клике по кнопке форма становиться неактивной до тех пор, пока все процессы по преобразованию картинок не закончатся. Чтобы этого не произошло, надо функцию запустить в отдельном процессе.
Свойство Factory класса Task возвращает объект TaskFactory. При вызове метода StartNow() ему передается делегат Action (здесь это скрыто подходящим лямбда-выражением), который указывает на метод, подлежащий вызову в асинхронной манере.
private void button2_Click(object sender, EventArgs e) { // Запустить новую "задачу" для обработки файлов. //Свойство Factory класса Task возвращает объект TaskFactory. //При вызове методаStartNow() ему передается делегат Action<T> //(здесь это скрыто подходящим лямбда-выражением), //который указывает на метод, подлежащий вызову в асинхронной манере. Task.Factory.StartNew(() =>{ ProcessFiles(); }); }
Теперь отменим запущенные процессы. Создадим объект CancellationTokenSource и используем его для остановки запущенных процессов, передавая его в составе объекта ParallelOptions запущенному процессу из пула процессов.
private CancellationTokenSource cancelToken = new CancellationTokenSource(); private void Cansel_Click(object sender, EventArgs e) { // Это будет использоваться для сообщения всем рабочим потокам// о необходимости останова! cancelToken.Cancel(); } private void ProcessFiles1() { // Использовать экземпляр ParallelOptions для хранения CancellationToken. ParallelOptions parOpts = new ParallelOptions(); parOpts.CancellationToken = cancelToken.Token; parOpts.MaxDegreeOfParallelism = System.Environment.ProcessorCount; // Загрузить все файлы *.jpg и создать новую папку для модифицированных данных. string[] files = Directory.GetFiles(@"D:\src1", "*.jpg",SearchOption.AllDirectories); string newDir = @"D:\ModifiedPictures\src1";Directory.CreateDirectory(newDir); try { // Обработать данные изображения в параллельном режиме! Parallel.ForEach(files, parOpts, currentFile => { parOpts.CancellationToken.ThrowIfCancellationRequested(); string filename = Path.GetFileName(currentFile); using (Bitmap bitmap = new Bitmap(currentFile)) { bitmap.RotateFlip(RotateFlipType.Rotate180FlipNone); bitmap.Save(Path.Combine(newDir, filename)); this.Invoke( (Action)delegate { this.Text = string.Format("Processing {0} on thread {1}", filename, Thread.CurrentThread.ManagedThreadId); } ); } } ); } catch (OperationCanceledException ex) { this.Invoke ( (Action)delegate { this.Text = ex.Message; } ); } }
Добавить комментарий