|
web
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
VB.NET Threaded DLLI've been trying to create a multithreaded dll to be used for simple error logging to a file. The DLL will need to be imported into a .NET framework based application (Wonderware). I am having a problem importing into wonderware, getting the error message "...Denotes a field where a class was expected". I carry out the threading using the Mutex object, can this be used in a DLL class? It also imports System.IO and System.Threading - could this be a factor? The DLL seems to work when I use a dummy VB application to call it - although it does get the logging mixed up (i.e. time not in sequence) but it does prevent any file locking errors. The structure of the class is as follows: Public Class logMessageToFile Private Shared FileLockMutex As New Mutex Private Class loggerClass Public strMessageText As String *Some more public vars declared here* Public Sub LogMessage() * use mutex here* * log messages to file* * release mutex * End sub End Class Public Shared Sub writeToLog(ByVal MsgText As String, *other params*) Dim cLog As New loggerClass Dim thread1 As System.Threading.Thread thread1 = New Threading.Thread(AddressOf cLog.LogMessage) cLog.strMessageText = MsgText * store other params* thread1.Start() End sub End class As you can see I had difficulty ensuring that the variables remained private to each thread so I [thought] had to use a class to store them. If anyone can see any issues as to why this wouldn't work then let me know and I will resolve them and try again in wonderware. Thanks in advance, Colly Mitchell Colly,
It doesn't sound like the error message has anything to do with threading to me. When do you get the message? Is it when you reference the assembly from Wonderware? Is it in code that you've written inside Wonderware? Also, I don't think the use of threads is appropriate here. You're creating a new thread on each and every log message. That's going to be slow. You probably don't need to use threads at all. Writing to the file should be very quick. If you still think you need asynchronous processing of log messages then I recommend using a single thread. That thread would more or less sit in an infinite loop waiting for log messages to appear in a queue. It would dequeue them one at a time and stick them in a file serially. Your writeToLog method would simply enqueue the message. Another thing, if you need to synchronize access to a shared resource the SyncLock keyword or Monitor class is usually better than using a Mutex. The Mutex is intended for use cases involving the synchronization of multiple processes. You can't make variables or classes private to thread. It just doesn't work that way. You can simulate it by using thread local storage though. Brian CollyMitch wrote: Show quoteHide quote > Hi, > > I've been trying to create a multithreaded dll to be used for simple > error logging to a file. The DLL will need to be imported into a .NET > framework based application (Wonderware). I am having a problem > importing into wonderware, getting the error message "...Denotes a > field where a class was expected". > > I carry out the threading using the Mutex object, can this be used in a > DLL class? It also imports System.IO and System.Threading - could this > be a factor? > > The DLL seems to work when I use a dummy VB application to call it - > although it does get the logging mixed up (i.e. time not in sequence) > but it does prevent any file locking errors. > > The structure of the class is as follows: > > Public Class logMessageToFile > Private Shared FileLockMutex As New Mutex > Private Class loggerClass > Public strMessageText As String > *Some more public vars declared here* > Public Sub LogMessage() > * use mutex here* > * log messages to file* > * release mutex * > End sub > End Class > Public Shared Sub writeToLog(ByVal MsgText As String, *other params*) > Dim cLog As New loggerClass > Dim thread1 As System.Threading.Thread > thread1 = New Threading.Thread(AddressOf cLog.LogMessage) > cLog.strMessageText = MsgText > * store other params* > thread1.Start() > End sub > End class > > As you can see I had difficulty ensuring that the variables remained > private to each thread so I [thought] had to use a class to store them. > If anyone can see any issues as to why this wouldn't work then let me > know and I will resolve them and try again in wonderware. > > Thanks in advance, > > Colly Mitchell Brian,
Firstly thank-you for your feedback. I get the message when it tries to compile. The reason I had used a threaded solution was because the process could only take a maximum of 500msec and a slow logging process couldn't be allowed to affect that. I had thought that i would call the logger and let it run in it's own thread, allowing execution to continue in the main application. I will probably use a database based error log in future if I got this to work. I like the Local thread storage technique, I knew there had to be a better way to avoid each variable being overwritten by other thread calls. Anyway, I agree that using a queue to log messages would be a much more efficient solution, as there may be many attempts to log messages within the 500msec window. I presume then that I would call this logger at the beginning of the process and it would be constantly running but as a separate thread, reading from the queue and writing to a file? I can pass a user defined type to a queue? Would it be best to stop the logger thread when the queue is empty and call it again when I add to the queue (if there is not a thread running already)? Thanks for your help, Colly CollyMitch wrote:
> Brian, It sounds like a syntax error to me. Fortunately, if you look hard> > Firstly thank-you for your feedback. > > I get the message when it tries to compile. > enough you'll eventually find it. > The reason I had used a threaded solution was because the process could I don't think you need TLS for your particular situation. It's rarely> only take a maximum of 500msec and a slow logging process couldn't be > allowed to affect that. I had thought that i would call the logger and > let it run in it's own thread, allowing execution to continue in the > main application. I will probably use a database based error log in > future if I got this to work. > > I like the Local thread storage technique, I knew there had to be a > better way to avoid each variable being overwritten by other thread > calls. > used anyway. > Anyway, I agree that using a queue to log messages would be a much more Yes. A single worker thread approach is definitely the way to go here.> efficient solution, as there may be many attempts to log messages > within the 500msec window. > > I presume then that I would call this logger at the beginning of the You can either start the logger at the beginning of the process or you> process and it would be constantly running but as a separate thread, > reading from the queue and writing to a file? I can pass a user defined > type to a queue? Would it be best to stop the logger thread when the > queue is empty and call it again when I add to the queue (if there is > not a thread running already)? could use a lazy initialization technique. It doesn't really matter. Yes, the logger thread, once started, would run all the time. I wouldn't stop it when the queue empties because in most cases the queue would empty before the next log message was queued. Here's some code to get you started. My VB is a little rusty since I'm a C# guy. Public Class Logger ' You'll need to write your own BlockingQueue class. Private m_Queue As BlockingQueue = New BlockingQueue Private m_Thread As Thread = Nothing Public Sub Logger() m_Thread = New Thread(AddressOf LoggerThread) m_Thread.IsBackground = True m_Thread.Start() End Sub Public Sub WriteToLog(ByVal message As String) ' Put the message in the queue. This will wake the logger thread. m_Queue.Enqueue(message) End Sub Private Sub LoggerThread() Do While True ' The line will block until an item appears in the queue. Dim message As String = DirectCast(m_Queue.DequeueWait(), String) WriteMessageToFile(message) Loop End Sub End Class The only thing I haven't provided you is the BlockingQueue. The blocking queue would implement the standard producer-consumer pattern and provide a DequeueWait method that blocks if the queue is empty. I posted an example implementation in C# at... <http://www.gotdotnet.com/Community/UserSamples/Details.aspx?SampleGuid=BD9982D6-B14D-402B-B855-5D74D61ABC94> Brian Show quoteHide quote > > Thanks for your help, > > Colly Brian,
That's great thanks. I was going to ask how will I be able to add to the queue when the thread is removing from the queue at the same time. I would use the SyncLock method to lock the queue when adding and removing elements? The wait handle you use - this waits until the queue is unlocked and then adds/dequeues, this would mean that as soon as the queue was read the wait handle is reset to allow adding? 'Lock the queue SyncLock myQueue.SyncRoot msg = myQueue.Dequeue End SyncLock 'now unlocked WriteMessageToFile(msg) This locks the queue before Dequeuing > what happens when I try to add an element to the queue - i would need to use the same synclock. You use a wait handle to wait for it to be unlocked? >> _WaitHandle.WaitOne(); Thanks again for your help,Colly CollyMitch wrote:
> Brian, The WaitHandle is used to make the Dequeue method block if nothing is> > That's great thanks. I was going to ask how will I be able to add to > the queue when the thread is removing from the queue at the same time. > I would use the SyncLock method to lock the queue when adding and > removing elements? The wait handle you use - this waits until the queue > is unlocked and then adds/dequeues, this would mean that as soon as the > queue was read the wait handle is reset to allow adding? in the queue. The Enqueue method will signal the wait handle and the Dequeue method will wake up and try to dequeue an item before another thread does. If the queue is empty when the thread wakes (because another thread woke first) then it will loop back around to the WaitHandle.WaitOne() method again and sleep some more. > You should make your queue class thread-safe. That way you won't need> 'Lock the queue > SyncLock myQueue.SyncRoot > msg = myQueue.Dequeue > End SyncLock 'now unlocked > WriteMessageToFile(msg) > to use SyncLock inside the Logger class. I recommend taking the BlockingQueue class I have and just port it to VB. The synchronization is a bit tricky and it would save you a lot of time. > This locks the queue before Dequeuing > what happens when I try to add Again. The WaitHandle is used to make to the Dequeue method block if> an element to the queue - i would need to use the same synclock. You > use a wait handle to wait for it to be unlocked? > > >> _WaitHandle.WaitOne(); nothing is in the queue. It has nothing to do with locking the queue. That's what SyncLock (or lock in C#) do. Show quoteHide quote > > Thanks again for your help, > > Colly Brian,
I've got it working really well, really fast and it works in perfect sequence. I've used a synchronised queue and the AutoResetEvent. I didn't port you blocking class but did learn a lot from it. I've declared the thread as a background thread, if I didn't do this - would it continue to run in the infinite loop even when the calling application ended? Thanks again for your all help, Colly CollyMitch wrote:
> Brian, AutoResetEvent is fine. Though, the semantics of the queue may be> > I've got it working really well, really fast and it works in perfect > sequence. I've used a synchronised queue and the AutoResetEvent. I > didn't port you blocking class but did learn a lot from it. > slightly different than my approach which used ManualResetEvent. An even better approach is to use Monitor.PulseAll and Monitor.Wait. The following article demonstrates how this is done. <http://www.yoda.arachsys.com/csharp/threads/deadlocks.shtml> > I've declared the thread as a background thread, if I didn't do this - Yep. That's pretty much right. If you want to shutdown the thread> would it continue to run in the infinite loop even when the calling > application ended? > gracefully you'll have to modify the Logger class a bit. Maybe something like the following. Public Class Logger ' You'll need to write your own BlockingQueue class. Private m_Queue As BlockingQueue = New BlockingQueue Private m_Thread As Thread = Nothing Private m_Shutdown as ManualResetEvent = New ManualResetEvent(False) Public Sub Logger() m_Thread = New Thread(AddressOf LoggerThread) m_Thread.IsBackground = True m_Thread.Start() End Sub Public Sub Stop() m_Shutdown.Set() m_Thread.Join() End Sub Public Sub WriteToLog(ByVal message As String) ' Put the message in the queue. This will wake the logger thread. m_Queue.Enqueue(message) End Sub Private Sub LoggerThread() Dim keepGoing As Boolean = True Do While keepGoing Dim handles(2) as WaitHandle handles(0) = m_Shutdown handles(1) = m_Queue.WaitHandle Dim index as Integer = WaitHandle.WaitAny(handles) If index = 0 Then keepGoing = False Else Dim message As String = DirectCast(m_Queue.Dequeue(), String) WriteMessageToFile(message) End If Loop End Sub End Class
Derived TextBox
adding items to a listbox that are null problem Update an Access DB using VB.Net Generic path description - please help Folder size in VB.NET? Financial.Rate Cannot calculate rate using the arguments provided Abstract/Base Class Best Practices - Help VB 2005 .net- Login control - how do I validate user no / password against SQL server IntelliSense Code Comments? Instance of derived class does not show public methods or properties that are not in the inherited c |
|||||||||||||||||||||||