0

i want to create singleton that has its own thread, and executes every method (lets assume this is database, and every add/modify action need to be called from the same thread), that thread can be created inside constructor, also i want that every method from that singleton will execute on this specific thread.

From what i understand, the new System.Threading.Thread.Thread() gives me ability to start thread, but after start, i cannot freely ququee next work to it. How to queue new work to that thread? This should work like myThread.Queue(()=> doWork()); but i dont see api like that.

Paweł Kanarek
  • 2,317
  • 2
  • 10
  • 17
  • 2
    Look into blocking collection https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent.blockingcollection-1?view=netcore-3.1 – the.Doc Apr 29 '20 at 21:14
  • Some example what you mean "Threads and Handlers from Android" could broader number of people who know what you are asking... (also answer is likely "no" as there is no general way I know of to schedule work on arbitrary thread - there is specific code for UI threads in each framework... maybe looking into `SynchronizationContext` can help) – Alexei Levenkov Apr 29 '20 at 21:19
  • @AlexeiLevenkov I've removed reference to Android framework from topic. I want to get stricte c# & .net ideas. Including other platforms into question was mistake. Thanks. – Paweł Kanarek Apr 29 '20 at 21:27
  • 2
    Its a pretty intriguing requirement. Can you explain why you need 1 specific thread, and not for example queued tasks, executing 1 at a time? – Stefan Apr 29 '20 at 21:28
  • @the.Doc Blocking collection looks like good mechanism to queue tasks, but how achieve that every task dequeued from blocking collection is fired on the same thread? – Paweł Kanarek Apr 29 '20 at 21:33
  • @Stefan i have some resources that can be only updated from the same thread that were created. And i don't talk about main thread :) As this can take some time and i don't want to block UI. From documentaitons of Realm database: "(...)Objects can only be used on the thread on which they were created, otherwise an exception is thrown (...)" Longs tory short, i want my database to be singleton with single thread to manage all items inside it. That looks like good idea. – Paweł Kanarek Apr 29 '20 at 21:37
  • Ah, like that... does seem you're mixing UI with logic. Anyhow, you can wrap your component in an object containg its own thread. Rewire the original ckmponents actions to a running thread. Will be easy, will gurantee the same thread.... but is very smelly ;-) – Stefan Apr 29 '20 at 21:40
  • Singleton database sou ds like reusing/not closing a connection... thats often a bad idea... but, it will make the wrapping solution pretty doable. – Stefan Apr 29 '20 at 21:41
  • @Stefan your comment seem to be essentially the question - not sure what you wanted to comment on... – Alexei Levenkov Apr 29 '20 at 21:41
  • @Stefan but how to run anything after thread was stared? I got reference to thread: https://learn.microsoft.com/en-my/dotnet/api/system.threading.thread?view=netcore-3.1 but i don't see any Queue, Run, or Post methods.. i can only start thread – Paweł Kanarek Apr 29 '20 at 21:43
  • 3
    @PawełKanarek Your solution is to not hold onto database connections for a long time, across multiple logical operations. Instead create a new connection for each operation you want to perform, and then you have no need to perform all of your database work on one thread. Connection pooling already exists for basically every database provider precisely to prevent that approach from being a performance problem. Don't try to write your own (unoptimal) connection pool of size one. – Servy Apr 29 '20 at 21:56
  • @Servy, for sure, i don't want to see any of this code on production codebase. I want to create simple PoC that uses Realm Database in my appilcation that previously used Akavache database. I know there are mechanisms in Realm Database to access the data from mutiple threads, but this is a lot of work for me in current state of my codebase. If Realms wan'ts single thread i want to give it single thread. I want to create simple database from single thread and i want to update/create/modify from the same thread. And measure if it is worth to change database. Thanks for reply. – Paweł Kanarek Apr 29 '20 at 22:09
  • @PawełKanarek Creating a proof of concept using a completely incorrect approach, that you have no intention of actually using, isn't a useful proof of concept. – Servy Apr 29 '20 at 23:25
  • @Servy Of course, and i should start with HLD, LLD and Tests before writing any implementation code... Right now i'll focus only on measurements which database is faster in storing my application specific data models. If its slower then why i should do PoC in more sophisticated way? To waste my time? From what i see RN realm database has a lot more gotchas than threading support. Im doing MVP to estimate work, if any will be further invested. Thanks for help. – Paweł Kanarek Apr 29 '20 at 23:54
  • In case you have asynchronous delegates (`Func`) to execute instead of synchronous `Action`s, you could take a look at the [`ExecutionQueue`](https://stackoverflow.com/questions/60984105/queuing-asynchronous-task-in-c-sharp/60992804#60992804) class by Stephen Cleary (internally uses a `BlockingCollection` too). – Theodor Zoulias Apr 30 '20 at 03:35
  • yeah i did that :) – Paweł Kanarek Apr 30 '20 at 08:27
  • @PawełKanarek If you do a PoC with an improper implementation, than it being slower won't tell you that it's an ineffective tool, because you weren't using it properly. If you decide to do a proof of concept of how effective a hammer is by trying to use it to drive in screws, you'll come to the conclusion that it's terrible and will discard it before using it properly to drive in nails. A PoC should certainly be a simpler use case for a tool, but not an *improper* usage of that tool, for it to provide meaningful results. – Servy Apr 30 '20 at 12:46
  • Im checking how fast 1 hand can hammer nails, if it fits well, ill make 10 hands to do the same thing simultaneously. – Paweł Kanarek Apr 30 '20 at 13:32

1 Answers1

3

The below simplified code shows how you can do this. TryTake will block indefinitely (or unitl collection.CompleteAdding() is called, although there are overloads that accept a timeout value.

        var collection = new BlockingCollection<Action>();

        new Thread(() =>
        {
            while (collection.TryTake(out Action a, -1))
            {
                     a.Invoke();
            }
        }

        }).Start();
the.Doc
  • 867
  • 9
  • 17
  • Oh that looking nice! Will try it to improve it. Another question. Can i make this Thread idle, something like thread.SleepUntilNewWorkIsQueued? Because i don't want that nasty while loop to be running for whole application life. – Paweł Kanarek Apr 29 '20 at 21:50
  • 3
    `collection.TryTake()` is a blocking call so there is no busy waiting here – the.Doc Apr 29 '20 at 21:51
  • Me gusta very much mr the.Doc. thanks for idea! ill play with it :) – Paweł Kanarek Apr 29 '20 at 21:53
  • 3
    @the.Doc No, it's not. It will immediately return false if there is nothing to take, rather than blocking, as it says right in the documentation of the method. This will absolutely busywait, being an extreme drain on resources. – Servy Apr 29 '20 at 21:53
  • 2
    Why not use just [Take](https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent.blockingcollection-1.take?view=netcore-3.1)? It blocks the thread. – cassandrad Apr 29 '20 at 22:03
  • @cassandrad I prefer the `bool` return rather than the `Exception` when the collection is empty but it doesn't make a huge difference – the.Doc Apr 29 '20 at 22:11
  • @cassandrad That can block a thread as it wont be used anywhere else. Block a thread, wait until work will come, work appears, excecute it. Ill try to use Take() aswell – Paweł Kanarek Apr 29 '20 at 22:11
  • That did a job, every task/action from blocking collection is fired from the same thread. Thanks! – Paweł Kanarek Apr 29 '20 at 22:24
  • Instead of `while (collection.TryTake(out Action a, -1))` I would prefer to enumerate the collection with `foreach (var action in collection.GetConsumingEnumerable())`. Both are functionally equivalent, but the later is less mind-boggling IMHO. – Theodor Zoulias Apr 30 '20 at 03:23