在线程启动时调用的函数导致错误

时间:2013-10-29 23:00:52

标签: c# multithreading

我想我一定会错过一些直接盯着我的东西。

我正在通过另一个进程错误打开文件。

这是我的代码

Logger.cs:

public class Logger
{

    /// <summary>
    /// Log File Path 
    /// </summary>
    public string LogFilePath { get; set; }

    /// <summary>
    /// Writer
    /// </summary>
    public StreamWriter Writer { get; set; }

    // This is a reference to the form so that we can use its threadsafe Invoke to calls methods on non thread safe UI controls.
    private readonly Form _form;

    /// <summary>
    ///Parameterless  Constructor
    /// </summary>
    public Logger()
    {

    }

    /// <summary>
    /// Call this constructor with the reference to the form.
    /// </summary>
    /// <param name="form"></param>
    public Logger(Form form)
    {
        _form = form;
    }

    public void CreateLogFile()
    {
        try
        {
            String filePath = string.Format("{0:yyyy-MM-dd}", DateTime.Now);

            // This is the text file created with time stamps
            var folderName = ConfigurationManager.AppSettings["FolderToCreate"];
            String txtFile = string.Format("Log{0:yyyy-MM-dd hh-mm-ss-tt}", DateTime.Now) + ".txt";
            // Given in config to read the path 
            var localhostizedLetter = ConfigurationManager.AppSettings["localhostDriveLetter"] + "//";
            //Create directory
            string pathString = Path.Combine(localhostizedLetter, folderName);
            if (!Directory.Exists(pathString))
            {
                Directory.CreateDirectory(pathString);
            }
            // Create a folder inside directory 
            // If folder exists dont create it 
            pathString = Path.Combine(pathString, filePath);
            if (!Directory.Exists(pathString))
            {
                Directory.CreateDirectory(pathString);
            }
            // create a file inside d://DataSummarisationDatetime.now//datetimewithtimestamp.txt
            // if exists please dont create it.
            pathString = Path.Combine(pathString, txtFile);
            if (!Directory.Exists(pathString))
            {
                // here my file is created and opened.
                // so I m doing a try catch to make sure if file is opened we are closing it so that nother process can use it
                File.Create(pathString).Dispose();
                var fileInfo = new FileInfo(pathString);
                IsFileLocked(fileInfo);

            }
            LogFilePath = pathString;
        }
        catch (Exception exception)
        {

            throw exception;
        }


    }

    /// <summary>
    ///  To check if File is opened by another process.
    /// </summary>
    /// <param name="file"></param>
    /// <returns></returns>
    public bool IsFileLocked(FileInfo file)
    {
        FileStream stream = null;

        try
        {
            stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
        }
        catch (IOException)
        {
            //the file is unavailable because it is:
            //still being written to
            //or being processed by another thread
            //or does not exist (has already been processed)
            return true;
        }
        finally
        {
            if (stream != null)
                stream.Close();
        }

        //file is not locked
        return false;
    }

    /// <summary>
    /// Log message using textwriter
    /// </summary>
    /// <param name="logMessage"></param>
    /// <param name="w"></param>
    public static void Log(string logMessage, TextWriter w)
    {
        w.Write("\r\n" + DateTime.Now + " " + logMessage);
        w.Flush();
    }

    /// <summary>
    /// Call this function to log your message
    /// </summary>
    /// <param name="textLog"></param>
    public void Log(string textLog)
    {
        StreamWriter sw = null;
        if (!File.Exists(LogFilePath))
        {
            try
            {
                sw = File.CreateText(LogFilePath);
            }
            catch (Exception exception)
            {
                throw exception;
            }
            finally
            {
                sw.Dispose();
            }

        }
        try
        {

            using (StreamWriter w = File.AppendText(LogFilePath))
            {
                Log(textLog, w);
                w.Close();
            }

        }
        catch (Exception exception)
        {
            throw exception;
        }

    }

    /// <summary>
    /// Call this function when it says file is already been using somewhere
    /// </summary>
    public void Dispose(string path)
    {
        File.Create(path).Close(); //Will close underlying stream
    }
}

我的班级使用_logger:

 private void TimerRetailerFeedTick(object sender, EventArgs e)
    {
        //Are we already processing one?
        if (_doingRetailerFeed)
        {
            //Yes,dont start another
            return;
        }

        //Say we are doing one
        _doingRetailerFeed = true;
        _logger.CreateLogFile();
        var scrapeStat = new ScrapeStatRepository();
        var stringBuilder = new StringBuilder();
        var scrapeRepository = new ScrapeRepository();
        var manufacturers = ConfigurationManager.AppSettings["Manufacturers"];
        var countries = ConfigurationManager.AppSettings["Countries"];
        if (_maxThreads == 0)
        {
            _maxThreads = 1;
        }
        ThreadPool.SetMinThreads(_maxThreads, _maxThreads);
        ThreadPool.SetMaxThreads(_maxThreads, _maxThreads);

        var dueFeeds = scrapeRepository.GetAllDueRetailerfeedForApiManufacturersAndCountriesList(manufacturers,
                                                                                              "RetailerFeeds",
                                                                                              countries);
        // 5.   Get when the feed is last run i.e closeofplay(Datetime now) date minus the feed current updatedate got from databse and if  it is  
        //greater than 24 hours (Interval) then add it to the list of feeds that are due to run.

        // var countDoing = dueScrapes.Count();
        if (dueFeeds.Any())
        {
            _logger.Log(" Processing Retailer feeds on timer tick");
            _logger.Log(" Total due feeds to run : " + dueFeeds.Count());

            timerRetailerFeed.Interval = 15 * 60 * 1000;

            foreach (var feed in dueFeeds)
            {
                object scrapeObject = feed;
                var scrapeStatRow = scrapeStat.GetScrapeStatByScrapeId(feed.Id);
                int totalProductUrls = scrapeStatRow.TotalProductUrls;
                int urlsFound = scrapeStatRow.UrlsFound;
                int urlsNotFound = scrapeStatRow.UrlsNotFound;
                _logger.Log("   ");
                _logger.Log(" Feed details :" + feed);
                stringBuilder.Append(" && ");
                stringBuilder.Append("Retailer " + feed.Retailer.Description + " Has " + "Total Product Urls : " +
                                     totalProductUrls + "   Total Url's found : " + urlsFound +
                                     " Total Urls not found : " + urlsNotFound);

                //  stringBuilder.Append(" , ");
                //  _logger.Log(" Proceesing Retailer : " + feed.Retailer.Description);
                ThreadPool.QueueUserWorkItem(o => UpdateRetailer(scrapeObject, true));
                // _logger.Log(" Status :  finished");
            }

            //Say we have finished
            _logger.Log(" Finished doing Retailers on Timer Basis ");
            var filePath = _logger.LogFilePath;
            var systemName = Dns.GetHostName();

            _keepItDry.SendRetailerFeedNotification("Ended RetailerFeeds Interface on Server " + systemName,
                                                    stringBuilder.ToString(), filePath);

            _keepItDry.AddTolistBox(
                DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString() +
                ": Ended RetailerFeeds interface ", _listBoxLog);
            //_logger.Dispose(filePath);
        }
        _doingRetailerFeed = false;
    }

我从我的线程池调用UpdateRetailer

   private void UpdateRetailer(object scrapeObject, object value)
   {
       _logger.Log(" Writing   : " + downloadFileName);
   }

我得到异常,说进程被另一个进程使用,所以我尝试了这个:

 Invoke(new System.Action(() =>
        {
            _logger.Log(" Writing   : " + downloadFileName);
        })); 

但我仍然得到同样的错误。

任何建议都会有所帮助。

1 个答案:

答案 0 :(得分:1)

您需要同步写入。目前,您正在从多个线程调用_logger.Log,每个线程都会尝试打开该文件。处理此问题的一个选项是打开日志文件以便在启动时写入,将其存储到成员变量(sw),然后在每次要写入时锁定它。更好的选择是使用System.Diagnostics.TraceSource和朋友。

伪码:

private StreamWriter sw;

public void CreateLogFile()
{
     // ...
     sw = File.AppendText(filename);
}

public void Log(string text)
{
   lock (sw)
   {
      sw.writeLine(text);
   }
}