Я услышал, что я ++ не ориентирован на многопотоковое исполнение, ++ я ориентирован на многопотоковое исполнение?

Эту проблему можно рассматривать как ориентированный граф, где ребро от вершины A до другой вершины B представляет грузовик, идущий из A в B (или производителя, желающего перевезти груз от A до B), а вес кромки можно использовать для представления величины нагрузки (или грузоподъемности грузовика).

Вы можете использовать матрицу смежности для каждого производителя и перевозчика. Каждый раз, когда новая запись заполняется в любой из матриц (скажем, в матрице производителя), соответствующая запись проверяется, проверяется в другой матрице (матрице транспортера) и сравнивается нагрузка, чтобы увидеть, есть ли совпадение. 1110]

```python
class trans_struct(T_ID, T_Type, T_Length, Trucks, Price):
    def __init__(self, T_ID, T_Type, T_Length, Trucks, Price) 
    self.T_ID = T_ID
    self.T_Length = T_Length
    self.T_Trucks = T_Trucks
    self.T_Type = T_Type
    self.T_Price = T_Price

class man_struct(M_ID, M_Type, T_Length, Trucks, Loading_Time):
    def __init__(self, M_ID, M_Type, T_Length, Trucks, Loading_Time)
    self.M_ID = M_ID
    self.T_Length = T_Length
    self.T_Trucks = T_Trucks
    self.T_Type = T_Type
    self.T_Price = T_Price

dicti = {A:0, B:1, C:2} #dictionary to map places to integeral indexes
num_places = len(dicti)    
trans_mat = [[[] for __ in range(num_places)] for _ in range(num_places)]  #initialize transport matrix to a default value
manf_mat = [[[] for __ in range(num_places)] for _ in range(num_places)]


def manf_input():
    #take input for manufacturer's data in this func into the structure object
    manf_mat[dicti[A]][dicti[B]].append((struct.T_Weight, struct))  #assuming manufacturer wanted to move goods from A to B
    check_for_match(A, B)     #function to compare corresponding matrix entries each time a new entry is inserted

def check_for_match(A, B, T_Length):
    for entry in trans_mat[dicti[A]][dicti[B]]:
        if  entry[0]>= T_Length:
            #match found display required info by retreiving from the structure
#
```


Я написал только некоторые функции здесь. Я написал только функцию, которая проверяет, когда для производителя создается новая запись, а не наоборот. Вы можете добавить дополнительные ограничения, такие как дата, время и т. Д.

89
задан Tim Williscroft 25 March 2009 в 04:52
поделиться

14 ответов

Вы услышали неправильно. Это может быть это "i++" ориентировано на многопотоковое исполнение для определенного компилятора и определенной архитектуры процессора, но она не получает мандат в стандартах вообще. На самом деле, так как многопоточность не является частью ISO C или стандартов C++ (a), Вы не можете полагать, что что-либо ориентировано на многопотоковое исполнение на основе того, что Вы думаете, что это скомпилирует вниз в.

Это довольно выполнимо это ++i мог скомпилировать в произвольную последовательность, такую как:

load r0,[i]  ; load memory into reg 0
incr r0      ; increment reg 0
stor [i],r0  ; store reg 0 back to memory

который не был бы ориентирован на многопотоковое исполнение на моем (мнимом) ЦП, который не имеет никаких инкрементных памятью инструкций. Или это может быть умно и скомпилировать его в:

lock         ; disable task switching (interrupts)
load r0,[i]  ; load memory into reg 0
incr r0      ; increment reg 0
stor [i],r0  ; store reg 0 back to memory
unlock       ; enable task switching (interrupts)

где lock отключает и unlock включает прерывания. Но даже затем это не может быть ориентировано на многопотоковое исполнение в архитектуре, которая имеет больше чем один из этих центральных процессоров, совместно использующих память ( lock может только отключить прерывания для одного ЦП).

Сам язык (или библиотеки для него, если это не встроено в язык) обеспечит ориентированные на многопотоковое исполнение конструкции, и необходимо использовать их, а не зависеть от понимания (или возможно недоразумение) того, какой машинный код будет сгенерирован.

Вещи как Java synchronized и pthread_mutex_lock() (доступный C/C++ под некоторыми операционными системами), то, что необходимо изучить (a).


(a) Этот вопрос задали перед C11 и C++ были завершены 11 стандартов. Те повторения теперь ввели поддержку поточной обработки в спецификации языка, включая атомарные типы данных (хотя они и потоки в целом, являются дополнительными, по крайней мере, в C).

155
ответ дан paxdiablo 24 November 2019 в 07:11
поделиться

Вы говорите, что "это - только одна инструкция, это было бы бесперебойно контекстным переключением". - это - все хорошо и хороший для единственного ЦП, но что относительно двухъядерного ЦП? Затем у Вас может действительно быть два потока, получающие доступ к той же переменной одновременно без любых контекстных переключений.

Не зная язык, ответ должен протестировать heck из него.

-1
ответ дан Jonathan Leffler 24 November 2019 в 07:11
поделиться

Стандарт C++ 1998 года не имеет ничего для высказывания о потоках, хотя следующий стандарт (должный в этом году или следующее) делает. Поэтому Вы не можете сказать ничего интеллектуального о потокобезопасности операций, не обращаясь к реализации. Это не просто используемый процессор, но и комбинация компилятора, ОС и модели потока.

В отсутствие документации наоборот, я не предположил бы, что любое действие ориентировано на многопотоковое исполнение, особенно с многоядерными процессорами (или многопроцессорные системы). И при этом я не доверял бы тестам, поскольку проблемы синхронизации потока, вероятно, подойдут только случайно.

Ничто не ориентировано на многопотоковое исполнение, если у Вас нет документации, в которой говорится, что это для конкретной системы, которую Вы используете.

1
ответ дан David Thornley 24 November 2019 в 07:11
поделиться

Никогда не предполагайте, что инкремент скомпилирует вниз в атомарную операцию. Используйте InterlockedIncrement или независимо от того, что подобные функции существуют на Вашей целевой платформе.

Править: Я просто искал этот конкретный вопрос, и инкремент на X86 является атомарным в единственных системах процессора, но не в многопроцессорных системах. Используя блокировку префикс может сделать это атомарным, но это намного более портативно только для использования InterlockedIncrement.

2
ответ дан Dan Olson 24 November 2019 в 07:11
поделиться

Если Ваш язык программирования ничего не говорит о потоках, уже работает на многопоточной платформе, как какой-либо язык может создать, ориентированы на многопотоковое исполнение?

Поскольку другие указали: необходимо защитить любой многопоточный доступ к переменным платформой определенные вызовы.

Существуют библиотеки там, что краткий обзор далеко специфика платформы и предстоящий стандарт C++ адаптировались, это - модель памяти для преодоления потоков (и таким образом может гарантировать потокобезопасность).

3
ответ дан xtofl 24 November 2019 в 07:11
поделиться

На x86/Windows в C/C++ Вы не должны предполагать, что это ориентировано на многопотоковое исполнение. Необходимо использовать InterlockedIncrement () и InterlockedDecrement () при требовании атомарных операций.

4
ответ дан i_am_jorf 24 November 2019 в 07:11
поделиться

Если Вы хотите атомарный инкремент в C++, можно использовать C++ 0x библиотеки ( std::atomic тип данных) или что-то как TBB.

Был однажды время, когда в инструкциях по кодированию GNU было сказано, что типы данных обновления, что соответствие одним словом было "обычно безопасно", но что совет является неправильным при машинах SMP, неправильным при некоторой архитектуре, и неправильно при использовании оптимизирующего компилятора.


Разъяснить "обновляющий тип данных с одним словом" комментарий:

Для двух центральных процессоров на машине SMP возможно записать в ту же ячейку памяти в том же цикле и затем попытаться распространить изменение в других центральных процессорах и кэше. Даже если только одно слово данных пишется так, записи только берут один цикл для завершения, они также происходят одновременно, таким образом, Вы не можете гарантировать, какая запись успешно выполняется. Вы не получите частично обновленные данные, но одна запись исчезнет, потому что нет никакого другого способа обработать этот случай.

Сравнивать-и-подкачивать правильно координирует между несколькими центральными процессорами, но нет никакой причины полагать, что каждое переменное присвоение типов данных с одним словом будет использовать сравнивать-и-подкачивать.

И в то время как оптимизирующий компилятор не влияет, как загрузка и хранение компилируется, это может измениться, когда загрузка и хранение происходит, доставляя серьезные неприятности, если Вы ожидаете свои чтения, и пишет для случая в том же порядке, они появляются в исходном коде (самое известное, перепроверяемое, заблокировав, не работает в ванильном C++).

ПРИМЕЧАНИЕ. В моем исходном ответе также было сказано, что архитектура Intel 64 битов была повреждена имея дело с данными на 64 бита. Это не верно, таким образом, я отредактировал ответ, но мое редактирование утверждало, что микросхемы PowerPC были повреждены. Это верно при чтении непосредственных значений (т.е. константы) в регистры (см. два раздела, названные, "Загружая указатели" в соответствии со списком 2 и списком 4). Но существует инструкция для загрузки данных из памяти в одном цикле (lmw), таким образом, я удалил ту часть своего ответа.

8
ответ дан Max Lybbert 24 November 2019 в 07:11
поделиться

При совместном использовании даже интервала через потоки в многоядерной среде Вам нужны надлежащие барьеры памяти на месте. Это может означать использовать взаимно блокируемые инструкции (см. InterlockedIncrement в win32, например), или использование языка (или компилятор), который делает определенные ориентированные на многопотоковое исполнение гарантии. С переупорядочением инструкции уровня процессоров и кэшами и другими проблемами, если у Вас нет тех гарантий, не предполагают, что что-либо совместно использованное через потоки безопасно.

Править: Одна вещь, которую можно принять с большей частью архитектуры, состоит в том, что, если Вы имеете дело с правильно выровненными отдельными словами, Вы не закончите с отдельным словом, содержащим комбинацию двух значений, из которых делали пюре вместе. Если две записи произойдут по вершине друг друга, то каждый победит, и другой будет отброшен. Если Вы осторожны, можно использовать в своих интересах это и видеть, что или ++ я или я ++ ориентированы на многопотоковое исполнение в единственной ситуации читателя устройства записи/нескольких.

11
ответ дан Eclipse 24 November 2019 в 07:11
поделиться

Они оба небезопасны потоком.

ЦП не может сделать математики непосредственно с памятью. Это делает это косвенно путем загрузки значения из памяти и выполнения математики с регистрами ЦП.

я ++

register int a1, a2;

a1 = *(&i) ; // One cpu instruction: LOAD from memory location identified by i;
a2 = a1;
a1 += 1; 
*(&i) = a1; 
return a2; // 4 cpu instructions

++ я

register int a1;

a1 = *(&i) ; 
a1 += 1; 
*(&i) = a1; 
return a1; // 3 cpu instructions

Для обоих случаев существует состояние состязания, которое приводит к непредсказуемому, которое я оцениваю.

Например, давайте предположим, что существует два параллельных ++, я распараллеливаю с каждым a1 регистра использования, b1 соответственно. И, с контекстным переключением, выполняемым как следующее:

register int a1, b1;

a1 = *(&i);
a1 += 1;
b1 = *(&i);
b1 += 1;
*(&i) = a1;
*(&i) = b1;

В результате я не становлюсь i+2, это становится i+1, который является неправильным.

Для исправления этого, moden центральные процессоры обеспечивают некоторую БЛОКИРОВКУ, РАЗБЛОКИРОВАЛИ инструкции по CPU во время интервала, контекстное переключение отключено.

На Win32 используйте InterlockedIncrement (), чтобы сделать меня ++ для потокобезопасности. Это намного быстрее, чем доверие взаимному исключению.

15
ответ дан Martin G 24 November 2019 в 07:11
поделиться

Вы не можете сделать общий оператор или о ++ я или о я ++. Почему? Рассмотрите постепенное увеличение 64-разрядного целого числа в 32-разрядной системе. Если базовая машина не имеет квадратическое слово "загрузка, инкремент, хранилище" инструкция, увеличивая то значение собирается потребовать многоадресных команд, любая из которых может быть прервана контекстным переключением потока.

Кроме того, ++i не всегда, "добавьте тот к значению". На языке как C, увеличивая указатель на самом деле добавляет, что размер вещи указал. Таким образом, если i указатель на 32 байтовых структуры, ++i добавляют 32 байта. Принимая во внимание, что почти все платформы имеют "инкрементное значение в адресе памяти" инструкция, которая является атомарной, не у всех есть атомарное, "добавьте произвольное значение для оценки в адрес памяти" инструкции.

42
ответ дан Jim Mischel 24 November 2019 в 07:11
поделиться

Взгляните на сравнение, сделанное недавно разработчиками Python: http://wiki.python.org/moin/DvcsComparison . Они выбрали Mercurial по трем важным причинам:

Выбор в пользу Mercurial был сделан по трем важным причинам:

  • Согласно небольшому опросу, разработчики Python больше заинтересованы в использовании Mercurial. t гарантировать, что остальная часть вычислений будет согласованной, если вы не используете правильные блокировки.

    Если вы хотите поэкспериментировать самостоятельно, напишите программу, в которой N потоков одновременно увеличивают общую переменную M раз каждый ... if значение меньше N * M, то некоторое приращение было перезаписано. Попробуйте и с преинкрементом, и с постинкрементом и сообщите нам; -)

0
ответ дан 24 November 2019 в 07:11
поделиться

В качестве счетчика я рекомендую использовать идиому сравнения и замены, которая не блокируется и не поддерживает потоки.

Вот она в Java:

public class IntCompareAndSwap {
    private int value = 0;

    public synchronized int get(){return value;}

    public synchronized int compareAndSwap(int p_expectedValue, int p_newValue){
        int oldValue = value;

        if (oldValue == p_expectedValue)
            value = p_newValue;

        return oldValue;
    }
}

public class IntCASCounter {

    public IntCASCounter(){
        m_value = new IntCompareAndSwap();
    }

    private IntCompareAndSwap m_value;

    public int getValue(){return m_value.get();}

    public void increment(){
        int temp;
        do {
            temp = m_value.get();
        } while (temp != m_value.compareAndSwap(temp, temp + 1));

    }

    public void decrement(){
        int temp;
        do {
            temp = m_value.get();
        } while (temp > 0 && temp != m_value.compareAndSwap(temp, temp - 1));

    }
}
0
ответ дан 24 November 2019 в 07:11
поделиться

Бросить i в локальное хранилище потока; это не атомарно, но тогда это не имеет значения.

0
ответ дан 24 November 2019 в 07:11
поделиться

Даже если он сокращается до одной инструкции сборки, увеличивая значение непосредственно в памяти, он все равно не является потокобезопасным.

При увеличении значения в памяти аппаратное обеспечение выполняет операцию «чтение-изменение-запись»: оно считывает значение из памяти, увеличивает его и записывает обратно в память. Аппаратное обеспечение x86 не имеет возможности увеличения непосредственно в памяти; ОЗУ (и кеши) может только читать и сохранять значения, но не изменять их.

Теперь предположим, что у вас есть два отдельных ядра, либо на разных сокетах, либо совместно использующие один сокет (с общим кешем или без него). Первый процессор считывает значение, и, прежде чем он сможет записать обновленное значение, второй процессор считывает его. После того, как оба процессора запишут значение обратно, оно будет увеличено только один раз, а не дважды.

Есть способ избежать этой проблемы; Процессоры x86 (и большинство многоядерных процессоров, которые вы найдете) способны обнаруживать этот вид конфликта в оборудовании и упорядочивать его, так что вся последовательность чтения-изменения-записи выглядит атомарной. Однако, поскольку это очень дорого, это делается только по запросу кода, на x86 обычно через префикс LOCK . Другие архитектуры могут делать это другими способами с аналогичными результатами; например, связанное с загрузкой / сохранение-условное и атомарное сравнение и замена (последние процессоры x86 также имеют этот последний).

Обратите внимание, что использование volatile здесь не помогает; он только сообщает компилятору, что переменная могла быть изменена извне, и чтение этой переменной не должно кэшироваться в регистре или оптимизироваться.Это не заставляет компилятор использовать атомарные примитивы.

Лучше всего использовать атомарные примитивы (если они есть в вашем компиляторе или библиотеках) или выполнять приращение непосредственно в сборке (используя правильные атомарные инструкции).

3
ответ дан 24 November 2019 в 07:11
поделиться