具有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;以线程安全的方式写入时,在不锁定的情况下读取可能会导致重复。">
他很快就注意到了,但后来我们都转向了不同的方向。
感谢大家在这里的评论
不管你在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
}
);
}
希望这能有所帮助。