Форум программистов, компьютерный форум, киберфорум
C# .NET
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.55/11: Рейтинг темы: голосов - 11, средняя оценка - 4.55
Заблокирован

Метод Interlocked.Exchange(Double, Double) не гарантирует одинаковое значение переменной в разных потоках?

20.03.2024, 13:45. Показов 2563. Ответов 48

Студворк — интернет-сервис помощи студентам
Метод Interlocked.Exchange(Double, Double) обеспечивает атомарность операции присвоения нового значения переменной типа Double.
То есть, InterlockedExchange гарантирует, что изменение переменной и возврат ее исходного значения не будут прерваны другими потоками.
Но есть ли гарантия, что данная переменная будет после этого иметь одно и то же значение в разных потоках, в том числе, исполняемых на разных процессорах? Похоже, что нет.
Но если нет, то тогда для обеспечения одинакового значения переменной нужно применять lock?
И тогда, если блокировать код присвоения с помощью lock, то тогда Interlocked.Exchange уже нет смысла использовать?
Правилен ли вывод, что Interlocked.Exchange имеет смысл применять в том случае, когда не нужно, чтобы значение переменной во всех потоках было одинаковым (самым последним)?
А преимущество Interlocked.Exchange по сравнению с блокировкой lock в том, что Interlocked.Exchange не тормозит потоки?
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
20.03.2024, 13:45
Ответы с готовыми решениями:

[Error] cannot convert 'double (*)(double)' to 'double' for argument '1' to 'double pow(double, double)'
#include <iostream> #include <math.h> using namespace std; int main () { system("cls"); double x,y; system ("echo...

Ошибки error C2296: -: недопустимо, левый операнд имеет тип "double (__cdecl *)(double,double,double
Думаю из-за polp #include<iostream> #include<cmath> #include<cstdlib> using namespace std; double polp(double af,double...

Написать функцию int Search ( double A[], int n , double x), которая находит в массиве double A[n] элемент, значение которого равно x
Написать функцию int Search ( double A, int n , double x), которая находит в массиве double A элемент, значение которого равно x. Функция...

48
 Аватар для IamRain
4693 / 2701 / 734
Регистрация: 02.08.2011
Сообщений: 7,227
31.03.2024, 13:32
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от titan4ik Посмотреть сообщение
2) Если используется модификатор переменной volatile, то он запрещает перемещать операции через операцию с этой переменной в обе стороны - предшествующие и последующие операции такими и останутся.
А в старой статье по C# Memory Model такого не утверждается. То есть работает точно также как и Volatile.Read (нельзя читать позже в условном микротайминге), и Volatile.Write (нельзя писать раньше). То есть у класса Volatile и ключевого слова volatile абсолютно одинаковое в этом плане поведение - барьеры в памяти. Еще упоминалось acquire-release semantics (как раз про разницу поведения при чтении-записи в volatile поле).
А в обе стороны - это все CAS-операции (Interlocked class).

Цитата Сообщение от titan4ik Посмотреть сообщение
Even though the volatile write to y on thread 1 occurred before the volatile read of y on thread 2, thread 2 may still see y2 == 0. The volatile write to y does not guarantee that a following volatile read of y on a different processor will see the updated value.
Вот про это, кстати, не знал. Воспроизводится. Читатель не всегда подхватывает самое свежее значение.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
int state = 0;
var readerList = new List<int>();
var writerList = new List<int>();
const int LIMIT = 20;
 
Console.WriteLine("Running concurrent writer and reader");
var countdown = new CountdownEvent(2);
RunWriter();
RunReader();
countdown.Wait();
Console.WriteLine("Running completed");
Console.WriteLine($"Lists:\n\t Writer: {string.Join(" ", writerList)}\n\t Reader: {string.Join(" ", readerList)}");
 
void RunWriter() => ThreadPool.QueueUserWorkItem((_) => 
{
    while (state < LIMIT)
    {
        var newValue = Interlocked.Increment(ref state);
        writerList.Add(newValue);
        Console.WriteLine($"{DateTime.Now.TimeOfDay}: Written {newValue}");
    }
    countdown.Signal();
});
 
void RunReader() => ThreadPool.QueueUserWorkItem((_) =>
{
    var counter = 0;
    while (counter < LIMIT)
    {
        var r = Volatile.Read(ref state);
        readerList.Add(r);
        Console.WriteLine($"{DateTime.Now.TimeOfDay}: {r}");
        counter++;
    }
    countdown.Signal();
});
Добавлено через 1 минуту
У себя время от времени получаю такой вывод:
Bash
1
2
3
4
Running completed
Lists:
         Writer: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
         Reader: 1 2 3 4 5 6 7 8 9 10 11 12 13 13 13 14 15 16 17 18
Добавлено через 8 минут
Хотя последний хороший пример, который помню, на многопоточное изменение с использованием Volatile.Read - это пример из статьи Тауба на на Interleaved Task-и - классный трюк, который показывает, как можно ожидать Task-и в их хронологическом порядке.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static Task<Task<T>> [] Interleaved<T>(IEnumerable<Task<T>> tasks)
{
    var inputTasks = tasks.ToList();
 
    var buckets = new TaskCompletionSource<Task<T>>[inputTasks.Count];
    var results = new Task<Task<T>>[buckets.Length];
    for (int i = 0; i < buckets.Length; i++) 
    {
        buckets[i] = new TaskCompletionSource<Task<T>>();
        results[i] = buckets[i].Task;
    }
 
    int nextTaskIndex = -1;
    Action<Task<T>> continuation = completed =>
    {
        var bucket = buckets[Interlocked.Increment(ref nextTaskIndex)];
        bucket.TrySetResult(completed);
    };
 
    foreach (var inputTask in inputTasks)
        inputTask.ContinueWith(continuation, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
 
    return results;
}
Видимо, здесь цель Volatile.Read - именно в запрете на перестановку операций.

Добавлено через 1 минуту
А не, тут Interlocked - видимо, перепутал с какой-то другой его статьей. Сейчас уже не вспомню.
1
Заблокирован
31.03.2024, 13:37  [ТС]
Для себя сделал такой вывод.
Особенно, если приложение реалтайм,
НЕ нужно выделываться. Нужно стараться остаться в рамках "простого решения" - нужно изначально правильно строить временнУю последовательность запуска блоков кода. Чтобы вот этих проблем с изменением общих ресурсов не было вовсе. А к механизмам потокобезопасности прибегать только в случае невозможности простого решения. Собственно об этом и пишут "в книжках" - параллельный код не должен использовать общих ресурсов, которые могут быть изменены в ходе его выполнения. А лучше вообще не использовать общие ресурсы. Скопировать всё локально и тогда уже запускать.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16117 / 11238 / 2887
Регистрация: 21.04.2018
Сообщений: 33,038
Записей в блоге: 2
31.03.2024, 13:48
Цитата Сообщение от titan4ik Посмотреть сообщение
??? То есть, флаг по сути один - в центре. А из регионов (потоков) можно только посмотреть на флаг или дать команду на его смену.
??? и что в итоге???
Как я и писал выше, "синхронизация получения сообщений в центре" не гарантирует "синхронизация отправлений сообщений в регионе".
Если не использовать Volatile, то отправка сообщений в центр, вообще, не будет происходить. И каждый регион будет работать со своим флагом, думая, при этом, что это общий флаг.

Цитата Сообщение от titan4ik Посмотреть сообщение
Насколько я понимаю, interlockid может обеспечивать потокобезопасность.
"Потокобезопасноть" это не есть "синхронизация потоков".
Очень часто при реализации перового, сразу добавляют и реализацию второго. Из-за это многие начинают воспринимать эти термины как эквивалентные. Но это неверное представление.

Цитата Сообщение от titan4ik Посмотреть сообщение
В общем, похоже, что стране нужен герой, который в одном связном тексте кратко опишет все аспекты применения этих инструменты атомарности и синхронизации с учетом конкретного физического устройства процессоров. И всё это с учетом того что актуально на сегодняшний день.
Это ОООЧЕНЬ большая тема.
Со всеми нюансами выйдет небольшой учебник.

Цитата Сообщение от titan4ik Посмотреть сообщение
И слишком много противоречий в текстах и мнениях.
Для общего понимания, достаточно "Потокобезопасноть" это не есть "синхронизация потоков".

Добавлено через 3 минуты
А деталей очень много. Дать общих рекомендаций невозможно. На практике, синхронизация очень редко бывает нужна. Почти все примеры её использующие (90+% - это будет локирование), решают через синхронизацию задачу потокобезопасности.
0
 Аватар для IamRain
4693 / 2701 / 734
Регистрация: 02.08.2011
Сообщений: 7,227
31.03.2024, 14:04
Цитата Сообщение от titan4ik Посмотреть сообщение
НЕ нужно выделываться.
Ну есть функциональные требования и нефункциональные, в частности, к производительности. У всех приложений они разные. Одно дело когда пишешь сайтик, когда нужно разово что-то инициализировать какой-то ресурс (просто lock не глядя пихаешь и все).
Другое дело, когда пишешь какой-то софт для высокочастотного трейдинга, где нужно не просто потокобезопасно сделать, но еще и обеспечить максимальную реактивность (короче, быструю реакцию). Тут уже каждую строку кода будешь обдумывать, да еще и бенчмарки сверху писать. А до этого - проверки на корректность (unit test-ы).
1
Заблокирован
31.03.2024, 14:35  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Это ОООЧЕНЬ большая тема.
Со всеми нюансами выйдет небольшой учебник.
Я думаю, что можно уложиться в 10 страниц. Максимум. Самые основные сведения без детализации.
Лучше, если это будет 3 страницы. И трёх хватит. Но точных. Со строгими корректными формулировками.
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Для общего понимания, достаточно "Потокобезопасность" это не есть "синхронизация потоков".
В программировании, как сферы практической, есть большие проблемы с терминологией. Русские термины ещё и страдают тем, что объективно и субъективно ориентированы на англ язык.
Сейчас загуглил потокобезопасность и увидел сразу определение, которое включает и синхронизацию. Поэтому, Элд Хасп, раз уж такая пьянка, дайте корректное определение этим терминам. И поставим точку.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16117 / 11238 / 2887
Регистрация: 21.04.2018
Сообщений: 33,038
Записей в блоге: 2
31.03.2024, 16:18
Цитата Сообщение от titan4ik Посмотреть сообщение
дайте корректное определение этим терминам.
Это скорее к теоретикам. Я больше практик-любитель.
Поэтому корректности не гарантирую.

Потокобезопасность (Thread-Safe) — это термин, используемый в программировании для обозначения того, что определенный код или объект может безопасно использоваться в многозадачной среде, где выполняются несколько потоков исполнения.
Синхронизация потоков (thread synchronization) – это обобщенный термин, относящийся к процессу взаимодействия и взаимосвязи потоков.
Например.
Есть List<int>. Если в разных потоках в него добавлять элементы, то часть элементов пропадёт:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
    List<int> numbers = new List<int>();
    void  AddRange(int beg, int end, int step)
    {
        for (int i = beg; i < end; i+=step)
        {
            numbers.Add(i);
        }
    }
    Task[] tasks = new Task[10];
    for (int i = 0; i < 10; i++)
    {
        int k = i;
        tasks[k] = Task.Run(() => AddRange(k, 10_000, 10));
    }
    Task.WaitAll(tasks);
 
    WriteLine(numbers.Count); // Здесь вывод будет меньше 10_000
}
Добавим для потокобезопасности локирование:
C#
3
4
5
6
7
8
9
10
    void AddRange(int beg, int end, int step)
    {
        for (int i = beg; i < end; i += step)
        {
            lock (numbers)
                numbers.Add(i);
        }
    }
Получаем вывод точно 10_000. Потокобезопасность обеспечена.

Но на практике, у нас может быть задержка в выполнении, а результаты в листе нужно чтобы были последовательно. Реализация выше этого обеспечить не может:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
    List<int> numbers = new List<int>();
    void AddRange(int beg, int end, int step)
    {
        for (int i = beg; i < end; i += step)
        {
            lock (numbers)
                numbers.Add(i);
 
            // Эмуляция задержки из-за вычислений
            Thread.Sleep(Random.Shared.Next(40));
        }
    }
    Task[] tasks = new Task[10];
    for (int i = 0; i < 10; i++)
    {
        int k = i;
        tasks[k] = Task.Run(() => AddRange(k, 100, 10));
    }
    Task.WaitAll(tasks);
 
    WriteLine(numbers.Count);
Вот здесь нужна уже синхронизация потоков, чтобы поток, например, 3-ей задачи записывал после потока 2-й задачи.
Реализация синхронизации между потоками:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{
    List<int> numbers = new List<int>();
    ManualResetEventSlim[] slims = new ManualResetEventSlim[10];
    for (int i = 0; i < 10; i++)
    {
        slims[i] = new ManualResetEventSlim(false);
    }
    slims[9].Set();
    void AddRange(int beg, int end, int step)
    {
        for (int i = beg; i < end; i += step)
        {
            int prev = (beg + 9) % step;
            slims[prev].Wait();
            //lock (numbers)
            //WriteLine($"{beg}-{prev}: {i}");
            numbers.Add(i);
            slims[prev].Reset();
            slims[beg].Set();
 
            // Эмуляция задержки из-за вычислений
            Thread.Sleep(Random.Shared.Next(40));
        }
        slims[beg].Set();
    }
 
    Task[] tasks = new Task[10];
 
    for (int i = 0; i < 10; i++)
    {
        int k = i;
        tasks[k] = Task.Run(() => AddRange(k, 100, 10));
    }
    Task.WaitAll(tasks);
 
    WriteLine(numbers.Count);
}
Добавлено через 38 минут
В последнем, варианте, потокобезопасная работа с листом обеспечивается "автомоатически" за счёт синхронизации потоков.
0
Заблокирован
31.03.2024, 16:33  [ТС]
Ого! Солидный труд. Спасибо!
То есть, первый вариант кода работает с явной ошибкой - НЕ потокобезопасно
Второй вариант - данные не потеряны, но записаны не по порядку в список. Потокобезопасно. Тут виден критерий потокобезопасности - сохранение всех данных. Но если важен порядок, то код отработал не так, как нужно. То есть, потокобезопасность не гарантирует что такой код даст тот же результат, что и аналогичный однопоточный.
Третий вариант, если важна последовательность, то приходится добавлять средство синхронизации. И тут результат точно такой, каким был бы в однопоточном коде. Синхронизация соблюдена.

Из этого можно дать такие определения. Хотя, они вряд ли являются всеобъемлющими:
Потокобезопасным можно считать код, при работе которого не теряются данные.
Синхронизация - такое свойство кода, обеспечение которого гарантирует точно тот же результат, который дал бы аналогичный алгоритм в его однопоточном варианте.


Теперь посмотрим на те общие определения, которые Вы привели справочно, скорее как антиопределения (что можно понять из преамбулы) :
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Потокобезопасность (Thread-Safe) — это термин, используемый в программировании для обозначения того, что определенный код или объект может безопасно использоваться в многозадачной среде, где выполняются несколько потоков исполнения.
Это хороший пример определения, которое нельзя применять в таком изолированном виде. Потому что тут предполагается неявно, что читатель знает что такое "безопасное использование". А это вещь в себе. Один критерий безопасности очевиден - отсутствие потерь данных. Но могут (и должны быть ) и другие критерии. Это нам пока неизвестно. А в каком-то случае, ждя безопасности критически важен порядок следования данных. Но это уже епархия синхронизации.
Бракую это определение.
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Синхронизация потоков (thread synchronization) – это обобщенный термин, относящийся к процессу взаимодействия и взаимосвязи потоков.
Это вообще охренительное, я бы сказал, определение. Даже комментировать не берусь) Это фактически не определение.
ОК.
Но тем не менее, ясна суть.
Синхронизация обеспечивает идентичность результата при реализации алгоритма в однопоточном и многопоточном варианте и включает в себя потокобезопасность.
Потокобезопасность обеспечивает корректность операций записи и чтения переменных и отсутствие потери данных. Но не даёт однозначности результата, например, в плане последовательности данных.
Элд Хасп, спасибо!
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16117 / 11238 / 2887
Регистрация: 21.04.2018
Сообщений: 33,038
Записей в блоге: 2
31.03.2024, 16:51
Цитата Сообщение от titan4ik Посмотреть сообщение
Но могут (и должны быть ) и другие критерии. Это нам пока неизвестно.
Да. Могут быть.
Мне трудно сформулировать как-то обобщённо это.

Цитата Сообщение от titan4ik Посмотреть сообщение
Синхронизация обеспечивает идентичность результата при реализации алгоритма в однопоточном и многопоточном варианте и включает в себя потокобезопасность.
И да, и нет.
"Синхронизация" - это "временной" параметр. Так же как часы. Перед атакой все командиры синхронизируют свои часы. Но каждое подразделение будет действовать независимо (параллельно).
Совпадение с синхронным методом - это частный случай примера.
Как и в жизни. Одно подразделение доставило на точку груз, второе забрало точно по времени его оттуда и доставило дальше. Здесь можно провести какой-то эквивалент что одно подразделение могло сразу доставить до конечной точки.

Но например бой в котором сотня подразделений параллельно выполняет боевые задачи, но при этом некоторые должны быть синхронизированны по времени. Здесь уже параллель с выполнением одним подразделением не проведёшь.

Тоже самое автоматическая потокобезопасность. В примере - да, она получена. И почти всегда при синхронизации тоже будет получаться. НО! Не всегда. Поэтому, надо обращать внимание на этот фактор. И всегда анализировать, получена ли в конкретно этой задаче, в этой реализации потокобезопасность.

Самый простой пример, если бы было два списка, в один из которых в цикле вычисления записывается несколько значений, но их последовательность не важна.
0
Заблокирован
31.03.2024, 18:48  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
И да, и нет.
В контексте ваших примеров - да. В контексте приложений не реал тайм, не управляющих чем-то, а просто выдающих конечный результат - да.
А если это реал тайм или если взаимодействие во времени с чем-то ещё сторонним - то нет, конечно.
Но, тем не менее, такие определения общего рода должны быть. Иначе каждый раз, используя термины потокобезопасность и синхронизация надо явно ограничивать сферу их применения и явно говорить что конкретно имеется ввиду. Что неудобно.
В общем-то, вот это определение можно считать рабочим
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Потокобезопасность (Thread-Safe) — это термин, используемый в программировании для обозначения того, что определенный код или объект может безопасно использоваться в многозадачной среде, где выполняются несколько потоков исполнения.
При этом только нужно понимать под безопасностью использования именно конкретную безопасность данного кода в данном применении. Может это и мудро относить тяготы определения конкретных критериев безопасности конкретного кода в конкретных условиях выполнения на совесть разработчика. Проще говоря, работа приложения должна соответствовать ТЗ.

Добавлено через 7 минут
И тогда становится понятно, что использование неких "элементов потокобезопасности" в одном коде (одних условиях) отвечает критериям потокобезопасности кода в целом, а в других - нет.
И элементы эти в документации могут называться потокобезопасными, но с указанием того, что он не гарантируют обеспечения потокобезопасности кода в целом. Так и есть примерно.
Ещё лучше их было бы называть элементы ДЛЯ потокобезопасности.
Это как ремень безопасности в авто. Он для безопасности, но его использование безопасности не гарантирует.

Добавлено через 1 минуту
Всё, заканчиваю словоблудить)
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
31.03.2024, 18:48
Помогаю со студенческими работами здесь

Ошибка: error LNK2001: unresolved external symbol "double __cdecl Akk(double,double,double)"
#include &lt;iostream&gt; #include &lt;cmath&gt; using namespace std; double Akk(double x, double y, double z); int main() { int a, b, c; ...

Отметьте 3 ошибочных объявления переменной типа double, Double
Добрый день! Пожалуйста, помогите разобраться. Отметьте 3 ошибочных объявления переменной типа double, Double 1)double d1 = 0.0f;...

Отметьте 3 ошибочных объявления переменной типа double, Double
Добрый день! Пожалуйста, помогите разобраться. Отметьте 3 ошибочных объявления переменной типа double, Double 1)double d1 = 0.0f;...

Перегрузите метод f так, чтобы соответствовала виду static void f (double x, out double y)
ПОМОГИТЕ!! Выдаёт ошибку! До передачи управления из текущего метода выходному параметру &quot;y&quot; должно быть присвоено значение. ...

Перегрузите метод f так, чтобы его сигнатура соответствовала виду static void f (double x, out double y)
Как сделать метод, чтобы он соответствовал заданию? class Program { static double f(double x) { ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
49
Ответ Создать тему
Новые блоги и статьи
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Как объединить две одинаковые БД Access с разными данными
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru