具有System.IO StreamWriter类的线程安全代码

本文关键字:线程 安全 代码 System IO StreamWriter 具有 | 更新日期: 2023-09-27 18:00:51

我想生成1到10万个数字来获得文件名的唯一ID。为了实现这一点,我使用锁创建了一个静态属性,如下所示:-

 private static readonly object objLock = new object();
        private static int _statInt = 1;
        private static string statInt
        {
            get
            {
                lock (objLock)
                {
                    if (_statInt >= 100000)
                    {
                        _statInt = 1;
                    }
                    else
                    {
                        _statInt = _statInt + 1;
                    }
                    return "_" + _statInt.ToString();
                }
            }
        }

注意我不想生成唯一的id throw guid,因为它可能是重复的或日期-时间的组合[我尝试了这两种方法,但它正在创建重复]。在我的情况下,如果上面的方法在10万之后失败,那对我来说就是文件。[作为上面的代码,我将使用唯一的文件名,并且文件在大约5000的一批中创建,然后它被删除]

第一个问题以上代码线程安全吗?

如果是,那么我的第二个也是最初的问题从这里开始:-

我只是在这里保留完整的代码,以便更好地理解这个问题:-

class Program
    {
        static void Main(string[] args)
        {
            _Interfaceupload obj = new _Interfaceupload();
            for (int i = 0; i < 100000; i++)
            {
                Thread thrd = new Thread(obj.getFileNoDuplicate); 
                thrd.Start();
            }
            Console.ReadLine();
            Dictionary<string, string> obj0 = _Interfaceupload.objDic;
            Console.ReadLine();
        }
    }
    class _Interfaceupload
    {
        private static readonly object objLock = new object();
        private static int _statInt = 0;
        private static string _fileName = "C:/TEST/vikas";
        private static string _InitfileName = "C:/TEST/vikas";
        private static string statInt
        {
            get
            {
                lock (objLock)
                {
                    if (_statInt > 100000)
                    {
                        _statInt = 0;
                    }
                    else
                    {
                        _statInt = _statInt + 1;
                    }
                    return "_" + _statInt.ToString();
                }
            }
        }
        public static string stateDate
        {
            get
            {
                return "_" + DateTime.Now.Ticks.ToString() + "_" + System.Guid.NewGuid();
            }
        }
        public static Dictionary<string, string> objDic = new Dictionary<string, string>();
        public void getFileNoDuplicate()
        {
            try
            {
                //objDic.Add(_InitfileName + statInt, string.Empty);
                // _fileName = _InitfileName + stateDate;
                _fileName = _InitfileName + statInt;
                objDic.Add(FileManager2.Write(_fileName, "txt", "hello", false), string.Empty);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
class FileManager2
    {
        public static string Write(string file, string ext, string data, bool overwrite)
        {
            return (string)OperateOnFile(file, ext, data, overwrite);
        }
        private static object OperateOnFile(string file, string ext,
           string data, bool overWrite)
        {
            StreamReader sr = null;
            StreamWriter sw = null;
            string workingFile = null;
            string dir = null;
            try
            {
                workingFile = file + "." + ext;
                if (overWrite == false && File.Exists(workingFile))
                {
                    workingFile = (string)OperateOnFile(workingFile + System.Guid.NewGuid().ToString(), ext, data, overWrite);
                }
                else
                {
                    dir = "C:/TEST/";
                    if (!Directory.Exists(dir))
                        Directory.CreateDirectory(dir);
                    sw = new StreamWriter(File.Open(workingFile, FileMode.Create, FileAccess.Write, FileShare.None), Encoding.UTF8);
                    sw.Write(data);
                }
                return workingFile;
            }
            finally
            {
                if (sr != null)
                    sr.Close();
                if (sw != null)
                {
                    sw.Flush();
                    sw.Close();
                }
            }
        }
    }

(可能需要包含以下namespaces(

using System.Threading;
using System.IO;

第二个问题:当我在控制台应用程序(.NET framework 4.5(中运行它时,对于10万条记录,它看起来是重复的,因为我得到了异常"文件被另一个进程使用",但是如果第一个代码是线程安全的,那么它在10万之前不应该创建重复的id。我怎么称呼它?或者StreamWriter类或代码的问题正在绕过线程?不确定,请告知。

注意我无法锁定File.Exists方法。

已编辑在MarvinSmit的评论后,将方法作为非静态

  private string _fileName = "C:/TEST/vikas";
            private string _InitfileName = "C:/TEST/vikas";

还删除了字典并对其进行了如下修改:-

public void getFileNoDuplicate()
        {
            try
            {
                //objDic.Add(_InitfileName + statInt, string.Empty);
                // _fileName = _InitfileName + stateDate;
                _fileName = _InitfileName + statInt;
                FileManager2.Write(_fileName, "txt", "hello", false);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

之后这也没用。

解决方案

天啊!!我抓到了罪犯。。。以下代码行的问题

_fileName = _InitfileName + statInt;

我已经删除了这一行,并直接将其传递给方法。

public void getFileNoDuplicate()
        {
            try
            {
                FileManager2.Write(_fileName + statInt, "txt", "hello", false);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

这是一个疯狂的错误,我创建了该类的一个实例,并将其通过线程传递给

我感谢马文的评论,

"_fileName=_InitfileName+statInt;以线程安全的方式写入时,在不锁定的情况下读取可能会导致重复。">

他很快就注意到了,但后来我们都转向了不同的方向。

感谢大家在这里的评论

具有System.IO StreamWriter类的线程安全代码

不管你在FileStream周围放了多少锁,一个文件不能同时打开多次。这包括可能打开该文件的其他应用程序。您收到的异常告诉您,您正试图多次打开同一个文件——您的路径随机化代码已损坏。

也有例外-使用显式文件共享级别打开的文件。

您对FileManager2.OperateOnFile的每次调用都会尝试打开该文件。每个调用都是一个不同的线程。

sw = new StreamWriter(File.Open(workingFile, FileMode.Create, FileAccess.Write, FileShare.None), Encoding.UTF8);

你不能使用System.IO.Path.GetTempFileName有什么原因吗?它正是为您的情况而设计的。

在磁盘上创建一个唯一命名的零字节临时文件,并返回该文件的完整路径。

http://msdn.microsoft.com/en-us/library/system.io.path.gettempfilename%28v=vs.110%29.aspx

我认为你的问题在这里:

您将_fileName标记为static

private static string _fileName = "C:/TEST/vikas";

然后你有多个线程在这里为它赋值:

public void getFileNoDuplicate()
{
    try
    {
        //objDic.Add(_InitfileName + statInt, string.Empty);
        // _fileName = _InitfileName + stateDate;
        _fileName = _InitfileName + statInt;
        objDic.Add(FileManager2.Write(_fileName, "txt", "hello", false), string.Empty);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

一个线程可能会设置它,然后另一个线程会再次设置它,随后两个线程都会尝试写入同一个文件。放下static,你就会朝正确的方向出发。

然而,我同意Gusdor的观点,即您应该研究Parallel.For()Path.GetTempFileName()

我认为您可以通过使用以下代码(.Net 4+(来实现您想要的

    private static string _basePath = "C:/TEST/vikas/";
    static void Main(string[] args)
    {
        Parallel.For(0, 10000, (index) =>
            {
                string filename = Path.Combine(_basePath, "_" + index.ToString());
                // filename is unique
            }
        );
    }

希望这能有所帮助。