How to properly execute threads in C ++?

I have a rather large dynamic class of sparse matrix objects for writing, and I want to do the following: one thread handles the placement of elements in the matrix and one handles reading from the matrix.

The only time these two conflict is when they both want to access the same row / column at the same time. So I decided that a simple mutex lock for each row / column is enough.

Now this is the first time that I have really done threads in C / C ++, and I would like to do it from books, so to speak. I have two problems.

  • How do I create these threads? This is the language issue most.
  • How to implement locking as efficiently as possible? I believe that if there is a conflict, then the requesting thread will be queued and wait until the resource is released. However, how can I realize this awakening? I can conduct a survey in a memory location, but this is not elegant. Ideally, I think an interrupt-based approach would be best.
+4
source share
11 answers

If this is your first time doing multithreading, use the Boost.Threads library. Its semantics (including synchronization mechanisms) are very simple, and your implementation will be portable.

http://www.boost.org/doc/libs/1_42_0/doc/html/thread.html

+18
source

C ++ itself does not offer a thread. On Windows, you can use CreateThread . On UNIX, you can use POSIX threads (pthreads).

No need to create your own concurrency primitives. For example, on Windows, you can create a mutex object using CreateMutex and use WaitForSingleObject to wait until it is released.

+6
source

First of all, you do not need a mutex for each column and one per line. If you purchased a mutex for a row, you blocked all the cells in that row, so it doesn't matter which column you are accessing. Or, if you get a lock for a column, you locked all the cells in these columns, it doesn't matter which row. Thus, you can have a mutex per table, one per cell, one per row, or one per column. But one per line and one per column does not make sense.

Most synchronization primitives block your threads, and the thread will simply resume when the resource becomes free, you donโ€™t need to worry about signaling and waking up. This part is exactly what a synchronization object, such as a mutex or critical section, is for you.

The specifics of building and using synchronization primitives vary by platform. As others have already written, there are cross-platform libraries that you can use, but you must indicate which platform you are targeting, at least so that we know which libraries are available.

+3
source

I can answer part one quite simply - it depends on your platform. If you are using the Win32 API, see http://msdn.microsoft.com/en-us/library/ms682453%28VS.85%29.aspx "CreateThread" and read the examples. The book I read in multithreaded mode on Windows was as follows: http://www.amazon.co.uk/Windows-PRO-Developer-Jeffrey-Wintellect-Christophe/dp/0735624240 , which covers more than just threads using CreateThread , and another variant of BeginThread, but also locks and semaphones, etc.

If you are using Linux, you will need POSIX threads via the pthread function, see http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html for an example.

The sample code for pthreads looks like this: be careful, I left the functionality to create several threads from the same function, that is, to call the array of pthread_t variables. You may not need this.

 #include <pthread.h> #include <stdio.h> #include <unistd.h> #include <malloc.h> void *thread_function(void *arg) { /* DO SOME STUFF */ /* Exit Thread */ pthread_exit(); } int main(int argc, char** argv) { /* variables */ int retval = 0; /* array of thread handles */ pthread_t* thread_handle = (pthread_t*) calloc(1, sizeof(pthread_t));; /* create function - fork() for threads */ retval = pthread_create(&thread_handle[0], NULL, thread_function, NULL); /* DO SOME STUFF */ /* join - wait for thread to finish */ pthread_join(thread_handle[0], NULL); return EXIT_SUCCESS; } 

Compile with gcc filename -o fileexe -lpthread

+1
source

Are you sure you want to get the matrix, what you describe sounds like a queue. The lock for the queue is pretty simple: when someone reads or writes, they take an exclusive lock (via pthread_mutex). (Do not go into readwrite locks, etc., if you really do not know that you have problems with perforation)

Of course no interruption is required

+1
source

Start by increasing flows.

And as for your design, it looks like you decided to allow random access to the entire matrix from any stream.

If at all possible, it would be better to figure out how to allocate responsibility for parts of the matrix to specific flows. A lock call for each cell access will be a rather bottleneck.

+1
source

Thomas reviewed the thread library. The only reason you want to communicate with interrupt handlers is if you do not have an OS. Otherwise, use what the OS provides you with for flow control.

What I warned you about blocking is to be careful not to get into a dead end. You want to block rows and columns, so each row and each column must have its own mutex. (Actually, a read / write lock would be much better for performance, but you should be even more careful about deadlocks and race conditions.)

Make sure you always acquire and release locks in sequential order; you donโ€™t want the thread to block row N and then block the lock column K, and then the thread that locks column K decides to block row N and blocks, giving you two blocked threads and nothing happens (cue John Woo gunol standoff).

+1
source

A quick idea, select one of the available std :: threads implementations, and then look at std :: async and std::future and related tools.

+1
source

As for your second question, how to make a lock: put the thread to sleep and wake up, this will be done by the OS, this is not your concern. But there is a problem with your circuit.

You want to block access to a cell only if its row AND its column are locked. That is, allow access if the row OR column is not locked. This is usually not the way locks work. Also, if the row was locked, but you still allowed access (since the column was unlocked), you still want to lock it "more." That means you need more mutex.

The best implementation I can think of uses a single atomic counter for rows and a counter of state variables for columns. Upon access:

  • Increase the atomic row counter.
  • If the previous atomic counter value was zero, the row was unlocked. Access is ok.
    • The increment of the column condition variable. Ideally, this should not be blocked.
  • If the line counter was nonzero, it was blocked. Maybe a block in a column.
    • Wait until the column condition variable becomes zero. Before you release the mutex, set it to one.
  • When you finish accessing:
  • Reduce the condition variable (remember to block it) and skip it to wake up other calls if it becomes zero.
  • Atomically decreases the row counter.

This includes a little fancy work, but the total amount of blocking resources is generally quite low. Could someone else be better (or find a counterexample to my schema)?

Also note that the division of the matrix into rows and columns is somewhat arbitrary. If this scheme causes too many conflicts, you should probably split the lines into halves (for example).

+1
source

If you have a mutex for each row / column, you will have a race condition when the row stream and column read stream access the element at the row / column intersection. If you have a large matrix, you will need many mutexes. I am not sure that every operating system can provide thousands of mutexes.

It seems that the flow chain of consumer producers will be the best solution to your problem. Check out Intel's Stream Library (TBB) library . I find multithreading a lot easier when modeling your program using the data flow paradigm.

I want to reduce the memory size of large, sparse matrices, check out Boost.uBlas . It has a sparce_matrix template class that uses a map to store items associatively by index.

For efficiency reasons, you will not want to transfer entire matrices by copying to the producer-consumer queue. Instead, you will want to pass proxies to your sparse matrices. If TBB doesn't have any proxy setup yet, you can simply use boost :: shared_ptr. You may also want to have a pre-allocated pool of matrices that are processed in the "scheme" of the data stream.

0
source

Instead of having a separate stream for reading and writing (which always requires blocking), you can restrict the stream to access only certain elements of the matrix, for example, a single stream for half the rows and another stream for the last half (or one stream for an even row and one for an odd line) this way you can guarantee that the thread is never locked

0
source

Source: https://habr.com/ru/post/1300463/


All Articles