Results 1 to 9 of 9

Thread: SaveFileDialog in ConsoleApplication with list?

  1. #1

    Thread Starter
    Junior Member
    Join Date
    Jan 2018
    Posts
    18

    Exclamation SaveFileDialog in ConsoleApplication with list?

    So i have a multithreaded process in a Do until loop. and i have things i want to write to a text file everytime in that Do until loop. so my thaughts where that i create a list of strings and list.Add() in the Do Until loop. but my problem is that i dont know how to do that. can anybody give me some examples?

  2. #2
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    39,043

    Re: SaveFileDialog in ConsoleApplication with list?

    Is your thought that rather than writing to the file in the loop you'd just write to the list in the loop, then write the list to the file when the loop ends? That would almost certainly be better than having multiple threads contest for a file. The list would have to be at a scope such that all the threads can see it, and you'd probably want to use synchronization to protect the line of code that wrote to the list. I'm not sure whether a write to a list is thread safe otherwise, so you might as well be safe with it.

    So, since you mention a console application, you probably start with a Sub Main in a module. Just put the List(of String) in that module (but not in any method) and it will be accessible to all the threads. Also add some other object called something like LockObject at the same scope. This can be type Object. It's just used as what amounts to a baton. Each thread will grab this if it is available, write to the list, then release it. Any other thread that wants to write while the lock object is held by somebody else will wait until it becomes free. You don't have to deal with that, though.

    So, you'd likely want a write method just to make it cleaner, and it would look like this:
    Code:
    Public yourList As New List(of String)
    Public lockObject As New Object
    
    Private Sub WriteStuff(someString as string)
     SyncLock (lockObject)
      yourList.Add(someString)
     End syncLock
    End Sub
    My usual boring signature: Nothing

  3. #3
    PowerPoster JuggaloBrotha's Avatar
    Join Date
    Sep 2005
    Location
    Lansing, MI; USA
    Posts
    4,286

    Re: SaveFileDialog in ConsoleApplication with list?

    Quote Originally Posted by SoldierCrimes View Post
    So i have a multithreaded process in a Do until loop. and i have things i want to write to a text file everytime in that Do until loop. so my thaughts where that i create a list of strings and list.Add() in the Do Until loop. but my problem is that i dont know how to do that. can anybody give me some examples?
    What does any of this have to do with a SaveFileDialog as the title of the thread says?
    As for the lists and loops, see Shaggy's response.
    Currently using VS 2015 Enterprise on Win10 Enterprise x64.

    CodeBank: All ThreadsColors ComboBoxFading & Gradient FormMoveItemListBox/MoveItemListViewMultilineListBoxMenuButtonToolStripCheckBoxStart with Windows

  4. #4

    Thread Starter
    Junior Member
    Join Date
    Jan 2018
    Posts
    18

    Re: SaveFileDialog in ConsoleApplication with list?

    Quote Originally Posted by Shaggy Hiker View Post
    Is your thought that rather than writing to the file in the loop you'd just write to the list in the loop, then write the list to the file when the loop ends? That would almost certainly be better than having multiple threads contest for a file. The list would have to be at a scope such that all the threads can see it, and you'd probably want to use synchronization to protect the line of code that wrote to the list. I'm not sure whether a write to a list is thread safe otherwise, so you might as well be safe with it.

    So, since you mention a console application, you probably start with a Sub Main in a module. Just put the List(of String) in that module (but not in any method) and it will be accessible to all the threads. Also add some other object called something like LockObject at the same scope. This can be type Object. It's just used as what amounts to a baton. Each thread will grab this if it is available, write to the list, then release it. Any other thread that wants to write while the lock object is held by somebody else will wait until it becomes free. You don't have to deal with that, though.

    So, you'd likely want a write method just to make it cleaner, and it would look like this:
    Code:
    Public yourList As New List(of String)
    Public lockObject As New Object
    
    Private Sub WriteStuff(someString as string)
     SyncLock (lockObject)
      yourList.Add(someString)
     End syncLock
    End Sub
    The problem is that when i have this code:
    Code:
    Private Sub SaveStuff()
            SyncLock (lockObject)
                Dim saveFileDialog1 As New SaveFileDialog()
    
                saveFileDialog1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*"
                saveFileDialog1.FilterIndex = 2
                saveFileDialog1.RestoreDirectory = True
                If saveFileDialog1.ShowDialog() = DialogResult.OK Then
                    Using sw As StreamWriter = New StreamWriter(saveFileDialog1.FileName + ".txt")
                        For Each line As String In yourList
                            sw.WriteLine(line)
                        Next
                    End Using
                End If
            End SyncLock
        End Sub
    I get this error:
    Code:
    {"The current thread must be set to Single Thread Apartment (STA) mode before OLE calls can be made. Check that the Main STAThreadAttribute function is highlighted. This exception is only caused if an error detection program is linked to the process."}

  5. #5
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    39,043

    Re: SaveFileDialog in ConsoleApplication with list?

    Right. You won't be able to use THAT from a background thread, but you shouldn't need to, either. SaveFileDialog is only for asking the user for some information. That doesn't make sense on a background thread, especially if you have multiple threads running. If each item gets saved to a different file, then you have a totally different problem, but if all of this gets saved to ONE place, then just write to the list in the threads, and once all the threads have completed, THEN write to the file.

    If this is a long running process that you want to have save stuff periodically, then you need a different design. Get the file before your start the threads, then start the threads. Everything gets added to the list, which is accessible to all threads. In the UI thread, have a timer with a fairly long period. When the timer fires, lock the list, write it to the file, empty it, then release the lock. In other words, the UI thread gets the filenam at the start, the threads gather information, then the UI thread periodically writes stuff to the file. Leave the file writing and file name on the UI thread. You almost certainly don't want to be writing to the files on background threads, and you sure don't want to be getting the filename from any background thread. Those are UI tasks, so leave them there.

    Technically, writing to the file could also happen on a background thread, and that makes sense if writing takes significant time, but if that is the case, then you want the writing to happen on one dedicated thread, not on whichever thread happens to feel like it, cause that will just increase the potential for thread collision as different threads contend for the file access.
    My usual boring signature: Nothing

  6. #6

    Thread Starter
    Junior Member
    Join Date
    Jan 2018
    Posts
    18

    Re: SaveFileDialog in ConsoleApplication with list?

    Yeah i get it i think, but when should i Call my SaveStuff() then? right after the do loop causes the error because that has background threads.

  7. #7
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    39,043

    Re: SaveFileDialog in ConsoleApplication with list?

    That depends a bit on how you want the program to behave, which depends a lot on how concerned you are about a particular type of failure.

    Saving the data is essentially an anchor point. Once the data is saved to the file, it is safe against all but catastrophic loss (like disk failure, but at that point, this file is probably the least of your problems). So, how important is the data? I have some applications that are used in hostile environments (non-waterproof devices used over water). In that case, the application has to write as often as possible such that the data can be recovered even if the device is otherwise destroyed. In some other applications I just don't care about the data until it is all done and organized. In those cases, I won't write anything until the very end.

    You have to decide how you feel about it. The easiest thing to do is not to save anything until every thread has finished. The worst that can happen in that case would be that the data gets destroyed just before writing and is lost. If you can just run things again and get the data again, then you wouldn't care, and that would be great, because that lets you do what is easiest, which is to wait until every thread is finished, and only then do any writing. If the data is really critical, such that loss of any of it would be bad, then you'd have to save more often, which would be more involved. It all comes down to how you feel about it.

    If you can wait until all the threads have finished, that is easiest, but not necessarily easy, as you seem to have found out. If I understand it right, the loop spawns threads, which means that you can't just save the data after the loop has finished because the threads are likely still running at that time. Instead, you'd have to come up with a way to determine when the last thread has finished up. That may or may not be easy. If the threads are Thread objects, then you can check the status of each of them and JOIN on any running threads. If the threads are actually Tasks, then you could have a collection of Tasks and AwaitAll on that collection. Either way, the code outside the loop has to be able to see all the threads by some means such that it can determine whether they are still running or not, and how that can be done depends largely on how you wrote the code and what the threads are.
    My usual boring signature: Nothing

  8. #8

    Thread Starter
    Junior Member
    Join Date
    Jan 2018
    Posts
    18

    Re: SaveFileDialog in ConsoleApplication with list?

    Code:
    For i = 0 To thrednum
                    Dim trd = New Thread(Sub()
                                             Do Until loadedfile.Count = 0
                                                 'Some Code
                                             Loop
                                         End Sub) With {
                        .IsBackground = True
                                         }
                    threads.Enqueue(trd)
                    trd.Start()
                Next
    This is the code structure that i use.

    I have no idea where to put it in actually
    Last edited by SoldierCrimes; Jan 23rd, 2018 at 03:06 PM. Reason: Typo

  9. #9
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    39,043

    Re: SaveFileDialog in ConsoleApplication with list?

    Ok, so you have a Queue of threads. So, after the loop, you could simply JOIN on each of those threads in turn. You may not want to, but it would work in theory. If the thread has completed by the time you try to Join, the join will return immediately. If the thread is still running, though, then the UI thread will block on the Join statement until the other thread has completed. That's the issue. By the time your loop is done, it may be that the first few threads have completed, while others are still running, or maybe they are all running, or something like that. If you try to Join on them one after the other, Joining to those that are done won't take any time, but Joining to those still running will lock the thread that attempts the join, so your UI might appear frozen, or even stuttering, in the worst case.

    If you aren't doing anything with the UI thread at the time, then just joining like that will be fine. If you need the UI to remain responsive, then you'd have to start yet one more thread after the loop and in that new thread you could join all the existing threads, and after all the Joins return, then have that one thread take action (which might mean raising an event back to the UI thread).

    By the way, you might look into Task rather than creating threads. Threads are older, and they work quite nicely, but the whole Task library was added to make a lighter weight, easy to use, alternative. I can't claim to understand the difference fully, but a Task is supposed to run wherever it is most efficient according to the OS. Most likely, it will be on a different thread, but I understand that it doesn't HAVE to be. Also, there's supposed to be less overhead setting up and tearing down a task, which seems doubtful to me, since there isn't much cost anyways.

    With tasks, you have a lot of alternatives to Joining. With a collection of Tasks, you can WaitAll (essentially like joining to each thread in the queue, except that it would be just one statement), WaitAny (take action after even one of the tasks completes), and other options.

    Having said that, I'd also add that, since you have this written using Threads, I don't think there is any great reason to switch to using Tasks.
    My usual boring signature: Nothing

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width