none
Лог в файл в многопоточном приложении RRS feed

  • Вопрос

  • VB,NET - нужно вести лог-файл в многопоточном приложении

    делаю так, но появляется ошибка "Процесс не может получить доступ к файлу "C:\Event\ComPort1\Port_11-27-2013.txt", так как этот файл используется другим процессом."

    Dim f As New StreamWriter
    Try
    f = New StreamWriter("C:\Event\ComPort1\" & DateString & ".txt", True, System.Text.Encoding.GetEncoding(1251))
    f.WriteLine(strMsgText)
    Finally
    f.Close()
    End Try

    как мне реализовать данную задачу в рамках моего приложения?

    заранее благодарю.

    27 ноября 2013 г. 18:34

Ответы

  • "Получается, что если у меня 3 параллельных потока, то каждый поток будет иметь коллекцию в буфере?" - нет, у вас будет одна разделяемая между потоками.

    "А как создать данную коллекцию и получить на нее доступ для чтения и записи?" - вот вам отличный пример, похожий на тот который я собирался написать. Правда на C#.


    Сделаем содержимое сообщества лучше, вместе!

    28 ноября 2013 г. 9:18
    Модератор
  • У меня этот вопрос висел уже давно.
    Как-то однажды я его уже решил.
    Но было некогда, а нужно было скорее...
    В общем получилось сыро и некрасиво...
    Вот наконец случилась оказия и я к этому вопросу снова вернулся.
    Честно говоря, код, на который сослался Yatajga,
    я не понял и запустить не смог.
    Пришлось написать самому.
    Наконец-то и сам разобрался.
    Опять C# - сорри!
    Но у всех процедур в VB есть аналоги -
    разберетесь.

    В ниже приведенной программе 
    лог пишется в рич-текст-бокс,
    но поток сообщений можно направить в файл,
    это не принципиально.
    По нажатию кнопки btStart
    инициируются потоко-безопасная очередь сообщений и
    список независимых потоков, 
    генерирующих эти строковые сообщения,
    и инкапсулируется собственно лог.
    Каждый генератор сообщений циклически формирует
    свой поток сообщений и 
    со случайной величиной временной задержки
    направляет их в общую очередь.
    Каждое сообщение сопровождается установкой
    сигнального состояния события MessageIsReadyEvent,
    определенного в главном потоке.
    Это в свою очередь позволяет потоку,
    выводящему сообщения в лог,
    полностью освободить накопленную очередь сообщений.

    using System;
    using System.Threading;
    using System.Windows.Forms;
    using System.Collections.Generic;
    using System.Collections.Concurrent;
    namespace	_LogFile
    {	public	partial	class	MainForm : Form
    	{	public	MainForm()	{	InitializeComponent();	}
    		static	AutoResetEvent		MessageIsReadyEvent	= new AutoResetEvent ( false );
    		static	List <threadClass	>	ThreadLst	= new List<threadClass>	();
    		static	ConcurrentQueue <string> MessageQueue;
    		public	void		Log ()
    		{	while (true)
    			{	MessageIsReadyEvent.WaitOne();	
    				MessageIsReadyEvent.Reset();
    				Invoke
    				(	new MethodInvoker
    				(	() =>
    				{	string Result;
    					while (	MessageQueue.Count > 0	)
    					{	MessageQueue.TryDequeue(out Result);
    						rtbLog.AppendText (Result);	//	Собственно лог в RichTextBox-е. Можно записывать в файл.
    						rtbLog.ScrollToCaret();
    				}}));	
    		}	}	
    		private void	Form_Closing	( object o, FormClosingEventArgs e)	{	Environment.Exit(0);	}		//	Закрыть форму
    		private	void	btStart_Click	( object o, EventArgs e	)																		//	Button "Start"
    		{	MessageQueue = new ConcurrentQueue<string>();
    			for (int i=0;i<10;i++)	//	Запуск десяти независимых потоков, генераторов сообщений, определяемых классом threadClass
    			{	ThreadPool.QueueUserWorkItem
    				(	y => 
    					{	
    						ThreadLst.Add ( new threadClass (Thread.CurrentThread.ManagedThreadId.ToString()));
    			}	);	}
    			Action	actLog=new Action	(	Log	);
    			actLog.BeginInvoke	(	null,null );
    		}
    		public	class		threadClass	//	Непрерывный генератор сообщений
    		{	public	string	IDThread;
    			public	int	NMessage;
    			public	threadClass	(	string idThread		)
    			{	IDThread	=	idThread;
    				MessageIsReadyEvent = new AutoResetEvent	(	false	);
    				NMessage = 0;
    				Random r	= new Random();
    				while (true)
    				{	Thread.Sleep (	r.Next(100,2000) );
    					MessageQueue.Enqueue (DateTime.Now.ToString	( "hh:mm:ss" ) + " - Thread " +	IDThread + " : " + (NMessage++).ToString() + "\n");
    					MessageIsReadyEvent.Set();
    }	}	}	}	}









    28 ноября 2013 г. 18:53

Все ответы

  • Я как раз таки подумывал написать статью на эту тему, вопрос достаточно распространённый. Если будет время напишу. Вам нужно использовать блокировку, чтобы в каждый момент времени к файлу имел доступ один поток. Но это самое быстрое и не лучшее решение. Лучшим является буферизация сообщений в памяти и периодичесое скидывание их на диск. В этом случае блокировать нужно будет не доступ к файлу, а к коллекции которая будет содержать эти данные, это будет намного эффективней и быстрей. К сожалению пока нет времени привести код. Но общая идея такая. А в целом есть готовые логеры, в которых эта проблема решена.

    Сделаем содержимое сообщества лучше, вместе!

    27 ноября 2013 г. 18:46
    Модератор
  • Получается, что если у меня 3 параллельных потока, то каждый поток будет иметь коллекцию в буфере?

    А как создать данную коллекцию и получить на нее доступ для чтения и записи?

    28 ноября 2013 г. 8:05
  • "Получается, что если у меня 3 параллельных потока, то каждый поток будет иметь коллекцию в буфере?" - нет, у вас будет одна разделяемая между потоками.

    "А как создать данную коллекцию и получить на нее доступ для чтения и записи?" - вот вам отличный пример, похожий на тот который я собирался написать. Правда на C#.


    Сделаем содержимое сообщества лучше, вместе!

    28 ноября 2013 г. 9:18
    Модератор
  • вот если бы на VB.NET - цены бы ему не было ...
    28 ноября 2013 г. 9:30
  • Попробуйте сервис трансляции кода.

    Сделаем содержимое сообщества лучше, вместе!

    28 ноября 2013 г. 9:32
    Модератор
  • У меня этот вопрос висел уже давно.
    Как-то однажды я его уже решил.
    Но было некогда, а нужно было скорее...
    В общем получилось сыро и некрасиво...
    Вот наконец случилась оказия и я к этому вопросу снова вернулся.
    Честно говоря, код, на который сослался Yatajga,
    я не понял и запустить не смог.
    Пришлось написать самому.
    Наконец-то и сам разобрался.
    Опять C# - сорри!
    Но у всех процедур в VB есть аналоги -
    разберетесь.

    В ниже приведенной программе 
    лог пишется в рич-текст-бокс,
    но поток сообщений можно направить в файл,
    это не принципиально.
    По нажатию кнопки btStart
    инициируются потоко-безопасная очередь сообщений и
    список независимых потоков, 
    генерирующих эти строковые сообщения,
    и инкапсулируется собственно лог.
    Каждый генератор сообщений циклически формирует
    свой поток сообщений и 
    со случайной величиной временной задержки
    направляет их в общую очередь.
    Каждое сообщение сопровождается установкой
    сигнального состояния события MessageIsReadyEvent,
    определенного в главном потоке.
    Это в свою очередь позволяет потоку,
    выводящему сообщения в лог,
    полностью освободить накопленную очередь сообщений.

    using System;
    using System.Threading;
    using System.Windows.Forms;
    using System.Collections.Generic;
    using System.Collections.Concurrent;
    namespace	_LogFile
    {	public	partial	class	MainForm : Form
    	{	public	MainForm()	{	InitializeComponent();	}
    		static	AutoResetEvent		MessageIsReadyEvent	= new AutoResetEvent ( false );
    		static	List <threadClass	>	ThreadLst	= new List<threadClass>	();
    		static	ConcurrentQueue <string> MessageQueue;
    		public	void		Log ()
    		{	while (true)
    			{	MessageIsReadyEvent.WaitOne();	
    				MessageIsReadyEvent.Reset();
    				Invoke
    				(	new MethodInvoker
    				(	() =>
    				{	string Result;
    					while (	MessageQueue.Count > 0	)
    					{	MessageQueue.TryDequeue(out Result);
    						rtbLog.AppendText (Result);	//	Собственно лог в RichTextBox-е. Можно записывать в файл.
    						rtbLog.ScrollToCaret();
    				}}));	
    		}	}	
    		private void	Form_Closing	( object o, FormClosingEventArgs e)	{	Environment.Exit(0);	}		//	Закрыть форму
    		private	void	btStart_Click	( object o, EventArgs e	)																		//	Button "Start"
    		{	MessageQueue = new ConcurrentQueue<string>();
    			for (int i=0;i<10;i++)	//	Запуск десяти независимых потоков, генераторов сообщений, определяемых классом threadClass
    			{	ThreadPool.QueueUserWorkItem
    				(	y => 
    					{	
    						ThreadLst.Add ( new threadClass (Thread.CurrentThread.ManagedThreadId.ToString()));
    			}	);	}
    			Action	actLog=new Action	(	Log	);
    			actLog.BeginInvoke	(	null,null );
    		}
    		public	class		threadClass	//	Непрерывный генератор сообщений
    		{	public	string	IDThread;
    			public	int	NMessage;
    			public	threadClass	(	string idThread		)
    			{	IDThread	=	idThread;
    				MessageIsReadyEvent = new AutoResetEvent	(	false	);
    				NMessage = 0;
    				Random r	= new Random();
    				while (true)
    				{	Thread.Sleep (	r.Next(100,2000) );
    					MessageQueue.Enqueue (DateTime.Now.ToString	( "hh:mm:ss" ) + " - Thread " +	IDThread + " : " + (NMessage++).ToString() + "\n");
    					MessageIsReadyEvent.Set();
    }	}	}	}	}









    28 ноября 2013 г. 18:53