0

I'm creating a client-server structure where the server has a thread for every client. That specific thread only sends and receives data. In the main thread of the server I'd want to read the input that the clientthread received. But it's possible that that input is being modified by the clientthread at the same time the mainthread is reading. How would i prevent this? I have read on locks but have no idea how to implement them that way.

A second part of my question is: the clientthread is a loop that constantly reads from a networkstream, and thus blocks that thread until it can read something. But can i call a function from my main thread (that function would send something through that networkstream) that the existing clientthread (that is looping) must execute?

Sorry i can't give any code right now, but i think it's clear enough?

Wouter Standaert
  • 299
  • 1
  • 4
  • 13
  • 1
    I'm afraid this description is too vague. Code would be better. I can't see how you'd get more than very general advice from this. – usr Dec 15 '13 at 12:29
  • Actually it is just general advice i need :) – Wouter Standaert Dec 15 '13 at 12:38
  • 1
    You have to use locking to prevent accidents. Which is going to be *very* detrimental to throughput if you have a non-trivial number of client connections, all these threads are going to fighting over the lock. You solve that by not doing this, the best general advice. – Hans Passant Dec 15 '13 at 13:09
  • I should clarify, the input of one client should only be accessed by two threads: the clientthread (which handles IO) and the main thread (which will handle updating the world using the IO). So the clientthread only writes to the variable (that is unique for every clientthread) and the main thread ONLY reads that data. Is a lock still required? I should add that this happens in a loop where the main thread reads the input every 50 ms or so. – Wouter Standaert Dec 15 '13 at 13:19
  • I have edited your title. Please see, "[Should questions include “tags” in their titles?](http://meta.stackexchange.com/questions/19190/)", where the consensus is "no, they should not". – John Saunders Dec 15 '13 at 13:24

1 Answers1

1

It sounds like a producer-consumer design might be a good fit for your problem. In general terms, the client threads will put any received data into a (thread safe) queue and not modify it after that - any new data that arrives will go to a new slot in the queue. The main thread can then wait for new data in any of the queues and process it once it arrives. The main thread could either check on all the queues periodically, or (better) receive some sort of notification when data is placed in a queue, so that it can sleep while nothing is happening and won't eat CPU time.

Since you ask about locks: Here is a basic lock-based implementation as an alternative to queues, perhaps that will help you understand the principle

class IncomingClientData
{
    private List<byte> m_incomingData = new List<byte>();
    private readonly object m_lock = new object();

    public void Append(IEnumerable<byte> data)
    {
        lock(m_lock)
        {
            m_incomingData.AddRange(data);
        }
    }

    public List<byte> ReadAndClear()
    {
        lock(m_lock)
        {
            List<byte> result = m_incomingData;
            m_incomingData = new List<byte>();
            return result;
        }
    }
}

In this example, your client threads would call Append with the data that they have received, and the main thread could collect all the rececived data that arrived since the last check by calling ReadAndClear.

This is made thread-safe by locking all the code in both functions on m_lock, which is just a regular plain object - you can lock on any object in C#, but I believe this can be confusing and actually lead to subtle bugs if used carelessly, so I almost always use a dedicated object to lock on. Only one thread at a time can hold the lock on an object, so the code of those functions will only run in one thread at a time. For example, if your main thread calls ReadAndClear while the client thread is still busy appending data to the list, the main thread will wait until the client thread leaves the Append function.

It's not required to make a new class for this, but it can prevent accidents, because we can carefully control how the shared state is being accessed. For example, we know that it is safe to return the internal list in ReadAndClear() because there can be no other reference to the same list at that time.

Now for your second question: Just plain calling a method won't ever cause the method to run on a different thread, no matter which class the method is in. Invoke is a special feature of the WinForms UI thread, you'd have to implement that functionality yourself if you want to Invoke something in your worker threads. Internally, Invoke works by placing the code you want to run into a queue of all things that are supposed to run on the UI thread, including e.g. UI events. The UI thread itself is basically a loop that always pulls the next piece of work from that queue, and then performs that work, then takes the next item from the queue and so on. That is also why you shouldn't do long work in an event handler - as long as the UI thread is busy running your code, it won't be able to process the next items in its queue, so you'll hold up all the other work items / events that occur.

If you want your client threads to run a certain function, you have to actually provide the code for that - e.g. have the client threads check some queue for commands from the main thread.

Medo42
  • 3,821
  • 1
  • 21
  • 37
  • Thanks for such a complete answer on my abstract question! Now i have anidea of what to do, thanks again. But for the second part, i won't be able to do it with a delegate for example? – Wouter Standaert Dec 15 '13 at 13:58
  • You can use delegates, but the interesting question is what you'll *do* with them. – Medo42 Dec 15 '13 at 14:03
  • I have edited the second part of my question. Is this possible? How would i achieve this? Thanks in advance – Wouter Standaert Dec 15 '13 at 14:36
  • As long as you're blocking on the `NetworkStream.Read` call, you can't run any other code on that thread. However, the documentation of `NetworkStream` says that you're allowed to have a read and a write running simultaneously on different threads, so it might be an option to use *two* threads per client. Alternatively, you might want to look into the asynchronous read/write methods which might simplify what you want to do, in particular if you are targeting .NET 4.5. If you are, then look into `ReadAsync`/`WriteAsync` and the async/await keywords. Otherwise, look into `BeginRead`/`EndRead` – Medo42 Dec 15 '13 at 15:07