I would like to generate 1 to 0.1 million numbers to get a unique ID for my file name. To achieve that I created one static property by using lock as follows:-
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();
}
}
}
Note I don't want to generate unique id throw guid as it could be duplicate or combination of date time[I tried both it was creating duplicate]. And in my case if above method fails after 0.1 million, it is file for me.[As the above code I will use for unique file name, and file creates in a batch of around 5000, and after that it gets delete]
First Question Is the above code thread safe?
If yes then my second and original question starts from here:-
I am just keeping full code here to understand the issue better:-
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();
}
}
}
}
(may require following namespaces
to include)
using System.Threading;
using System.IO;
Second Question : When I am running it in Console Application (.NET framework 4.5), for 0.1 million records, it looks that file is getting duplicate as I am getting exception "file is using by another process", however if the first code is thread safe then it should not create duplicate id till 0.1 million. What's wrong here, the way I am calling it? or issue with StreamWriter class or code is bypassing thread? not sure please advise.
Note I can't lock File.Exists
method.
Edited After MarvinSmit's comment made methods as non static
private string _fileName = "C:/TEST/vikas";
private string _InitfileName = "C:/TEST/vikas";
Also removed the Dictionary and modified it as follows:-
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);
}
}
After this also didn't work.
Solution
OMG!! I got the culprit... problem with the following line of code
_fileName = _InitfileName + statInt;
I have removed this line, and directly passed it to the method.
public void getFileNoDuplicate()
{
try
{
FileManager2.Write(_fileName + statInt, "txt", "hello", false);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
It was the crazy mistake, I have created a single instance of the class and pass it to across the thread,
I appreciate Marvin's comment,
"_fileName = _InitfileName + statInt; being read without locking while being written to in a thread safe manner may lead to duplicates."
he noticed it very soon, but we both gone to different direction later.
Thanks for everyone's comment here