首页 > 解决方案 > 线程中断后无法删除文件

问题描述

我有点迷失了这个;我已经尝试了我所知道的一切来进行此类操作,但错误仍然存​​在。

我有一个 FileProcessor 类,它创建一个新线程、执行一些操作等;但是,即使在其中手动调用 Dispose() 和 Thread.Interrupt() 我似乎也无法在使用后删除文件。

首先,我在主线程上使用异步方法执行此代码;现在我已经切换到这个 FileProcessor 的线程,只是想在操作后删除这些文件。

我可以删除一个或两个文件,但是当它到达第三个文件时,它会抛出一个 System.IOEXception

我真的不知道我还能做什么。任何输入表示赞赏。我在 Dispose() 中使用 Worker.Join 并等待线程完成或 GC 结束它 - 但它们都没有发生。

谢谢

我的代码(尽可能减少)Form1:

using System.Collections.Generic;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        private bool RestartTimer;
        private bool ThreadRunning;
        FileProcessor TIFFtoXMLProcessor;
        FileProcessor CIP3toTIFFProcessor;
        List<string> files;
        public Form1()
        {
            InitializeComponent();
            TIFFtoXMLProcessor = new FileProcessor();
            RestartTimer = false;
        }
        private void BeginWork()
        {
            TIFFtoXMLProcessor.EnqueueFileName(@"C:\test\yourtestfile1.txt");
            TIFFtoXMLProcessor.EnqueueFileName(@"C:\test\yourtestfile2.txt");
            TIFFtoXMLProcessor.EnqueueFileName(@"C:\test\yourtestfile3.txt");
            files = new List<string>(TIFFtoXMLProcessor.fileNamesQueue);
            TIFFtoXMLProcessor.eventWaitHandle.Set();
            if(TIFFtoXMLProcessor.worker.IsAlive == false)
            {
                foreach(var item in files)
                {
                    System.IO.File.Delete(item);
                }
            }
        }
    }
}

文件处理器类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
using System.IO;

namespace WindowsFormsApp1
{
    class FileProcessor : IDisposable
    {
        public EventWaitHandle eventWaitHandle { get; private set; }
        public Thread worker { get; private set; }
        private readonly object locker = new object();
        public Queue<string> fileNamesQueue { get; private set; }
        public string currConversion { get; private set; }
        public bool JobComplete { get; private set; }        
        private CancellationTokenSource cancelParallelWorker;
        public string ColorSeparator { get; private set; }        
        private readonly TextBox tbStatus;
        public string outputFolder { get; private set; }
        List<string> filesgoingtorun;

        //var AvailableJobsDictionary = new Dictionary<string, List<string>>();
        //string nZones, string zWidth, string fzWidth, string lzWidth, string zAreaWidth, string zAreaHeight, double DPI
        public FileProcessor()
        {

            eventWaitHandle = new AutoResetEvent(false);
            fileNamesQueue = new Queue<string>();
            // Create worker thread
            worker = new Thread(Work)
            {
                IsBackground = true
            };
            cancelParallelWorker = new CancellationTokenSource();
            worker.Start();
        }
        public void EnqueueFileName(string FileName)
        {
            // Enqueue the file name
            // This statement is secured by lock to prevent other thread to mess with queue while enqueuing file name
            lock (locker) fileNamesQueue.Enqueue(FileName);
            // Signal worker that file name is enqueued and that it can be processed
            //eventWaitHandle.Set();
        }
        private void Work()
        {
            List<string> filesToWork = new List<string>();
            while (true)
            {
                string fileName = null;
                // Dequeue the file name
                lock (locker)
                    while (fileNamesQueue.Count > 0)
                    {
                        fileName = fileNamesQueue.Dequeue();
                        filesToWork.Add(fileName);
                        if (fileName == null) return;
                    }
                if (fileNamesQueue.Count == 0 && filesToWork.Count > 0)
                {
                    var tempList = new List<string>(filesToWork);
                    filesToWork.Clear();
                    ProcessJob(tempList);
                }

            }
        }
        private void ProcessJob(List<string> filesToWork)
        {
            try
            {
                JobComplete = true;
                switch (currConversion)
                {
                    case "TIF":
                        {
                            int j = 0;
                            foreach (var currJob in filesToWork)
                            {
                                //Series of tasks...
                                j++;
                            }
                            eventWaitHandle.WaitOne();
                            break;
                        }
                }
                JobComplete = false;
                Dispose();
            }
            catch (Exception conversionEx)
            {
                cancelParallelWorker?.Cancel();                
            }
        }   
        #region IDisposable Members
        public void Dispose()
        {
            // Signal the FileProcessor to exit
            EnqueueFileName(null);
            // Wait for the FileProcessor's thread to finish
            worker.Interrupt();
            // Release any OS resources
            eventWaitHandle.Close();
        }

        #endregion
    }

}

标签: c#multithreading

解决方案


您的代码对于您要执行的操作非常复杂,难怪您在某个地方留下了在不同线程上打开文件的句柄,这会阻止您的代码删除该文件。在无法复制这个问题的情况下,我什至可以开始弄清楚你应该做什么。

但这是我要建议的方法。

您应该使用 Microsoft 的反应式框架(又名 Rx)- NuGetSystem.Reactive.Windows.Forms并添加using System.Reactive.Linq;- 然后您可以执行以下操作:

public partial class Form1 : Form
{
    private Subject<string> _enqueue = new Subject<string>();
    private IDisposable _subscription = null;

    public Form1()
    {
        InitializeComponent();

        string ColorSeparator = "42";
        int imageRotationNumber = 42;

        IObservable<string> query =
            from file in _enqueue
            from ImageListSorted in Observable.Start(() => ImageBuilder(file, ColorSeparator))
            from RotateCMYK in Observable.Start(() => Rotate(ImageListSorted.CMYKmages, imageRotationNumber))
            select file;

        _subscription = query.Subscribe(f => System.IO.File.Delete(f));

        _enqueue.OnNext(@"C:\test\yourtestfile1.txt");
        _enqueue.OnNext(@"C:\test\yourtestfile2.txt");
        _enqueue.OnNext(@"C:\test\yourtestfile3.txt");
    }


    private CreateCMYKAndImpositionImageList ImageBuilder(string JobImages, string colorDelimiter)
    {
        return new CreateCMYKAndImpositionImageList(JobImages, colorDelimiter);
    }

    private RotateImages Rotate(Dictionary<string, string> imageList, int RotationNumber)
    {
        return new RotateImages(imageList, RotationNumber);
    }
}

现在,我在您的流程中只包含了两个步骤,但您应该能够通过其余步骤继续逻辑。

每一步都是异步运行的,整个事情可以随时通过调用来取消_subscription.Dispose();

.Subscribe(f => System.IO.File.Delete(f))完成所有步骤后才能进入决赛。

因此,只要您避免与线程和任务相关的任何事情,那么它应该运行得非常干净。


推荐阅读