Код Go internal/poll/fd_unix.go находится здесь
// Write implements io.Writer.
func (fd *FD) Write(p []byte) (int, error) {
if err := fd.writeLock(); err != nil {
return 0, err
}
defer fd.writeUnlock()
......
}
код Java java.net.SocketOutputStream#socketWrite находится здесь
private void socketWrite(byte b[], int off, int len) throws IOException {
if (len <= 0 || off < 0 || len > b.length - off) {
if (len == 0) {
return;
}
throw new ArrayIndexOutOfBoundsException("len == " + len
+ " off == " + off + " buffer length == " + b.length);
}
FileDescriptor fd = impl.acquireFD();
try {
socketWrite0(fd, b, off, len);
} catch (SocketException se) {
......
Я не знаю, почему мы должны запирать это. Другой вопрос, syscall.Write эквивалентен записи <unistd.h> в C?
Однажды я написал аналогичный код на C, используя 4 потока для записи в сокет, но последовательность, считанная другим потоком, верна, поэтому я думаю, что этот write системный вызов сам по себе является потокобезопасным.
Можете ли вы гарантировать это для всех возможных поддерживаемых платформ сейчас или в будущем? Задокументировано ли такое поведение или это просто деталь реализации?
Кажется, я понимаю. Что вы имеете в виду, что возможно, что write некоторых платформ не гарантирует аналогичные характеристики атомарности?
«Другой вопрос: syscall.Write эквивалентен записи <unistd.h> в C?» -- Это должен быть отдельный вопрос.
@Flimzy Да, этот вопрос мне тоже помог. Спасибо




Хорошо, предположим, что нет механизма блокировки и два параллельных потока/процесса записывают некоторые данные в сокет. Возьмем упрощенный пример.
Java-приложение хочет написать: «Привет! Как дела?».
Приложение Go хочет написать: «Увидимся позже».
Оба этих приложения пытаются писать одновременно.
Ожидаемый результат:
Hello! How are you?
See you later.
Тем не менее, есть большая вероятность, что вы получите что-то подобное.
HellSee o! How See you are later.
you?
Всякий раз, когда ресурс совместно используется различными процессами/потоками и отсутствует механизм блокировки. Существует высокая вероятность столкнуться с непоследовательным и неожиданным поведением.
спасибо за ответ, но я знаю, что если нет защиты/блокировки сокета/fd, последовательность байтов записи будет беспорядочной. Я, наверное, понимаю, что в ядре есть буфер записи при записи в какой-то сокет, разве это не буфер потокобезопасный?
Независимо от того, является ли буфер потокобезопасным, параллельные записи в буфер все равно будут чередоваться, поэтому буфер будет содержать чередующиеся данные. Тот факт, что буфер существует, также не означает, что он потокобезопасен; это может быть, а может и не быть, как деталь реализации.
Ну, только отвечая за часть Java:
Поскольку мы уже говорим о деталях реализации, зачем останавливаться на уровне Java? Исходный код C для OpenJdk реализации нативного метода socketWrite0 занимает около 70 строк кода и явно не является атомарным. Он выполняет такие вещи, как выделение и освобождение памяти с помощью malloc и free, среди прочего, и включает довольно много нетривиальной логики. На данный момент уже не имеет значения, будет ли функция NET_Send, которую он вызывает для фактической отправки данных, напрямую транслироваться в системный вызов на каждой поддерживаемой платформе.
Суть в том, что реализация вызывает эту NET_Send-функцию в цикле. Таким образом, независимо от того, является ли это потокобезопасным по отдельности, если он вызывается одновременно несколькими потоками, вывод будет чередоваться (в лучшем случае).
ооо, чем вы так много ,я узнал об этом через ваш ответ, и пойти тоже должно быть причиной. Потому что я обнаружил, что Go также циклически вызывает syscall.Write в приведенном ниже коде. for { max := len(p) if fd.IsStream && max-nn> maxRW { max = nn + maxRW } n, err := ignoringEINTR(func() (int, error) {return syscall.Write(fd.Sysfd, p[nn:max]) }) if n> 0 { nn += n }
А что бы вы хотели, чтобы два потока вызывали этот метод одновременно?