Список вопросов
Как зайти в Даркнет?!
25th January, 01:11
5
0
Как в tkinter из поля ввода Entry получить значение в одну переменную и обновить строку кнопкой, затем получить ещё одно введённое значение и затем сложить их. Ниже пример кода
21st July, 19:00
893
0
Программа, которая создает фейковые сервера в поиске игровых серверов CS 1.6 Steam
21st March, 17:43
948
0
Очень долго работает Update запрос Oracle
27th January, 09:58
912
0
не могу запустить сервер на tomcat HTTP Status 404 – Not Found
21st January, 18:02
905
0
Где можно найти фрилансера для выполнения поступающих задач, на постоянной основе?
2nd December, 09:48
938
0
Разработка мобильной кроссплатформенной военной игры
16th July, 17:57
1724
0
период по дням
25th October, 10:44
3955
0
Пишу скрипты для BAS только на запросах
16th September, 02:42
3720
0
Некорректный скрипт для закрытия блока
14th April, 18:33
4613
0
прокидывать exception в блоках try-catch JAVA
11th March, 21:11
4381
0
Помогите пожалуйста решить задачи
24th November, 23:53
6086
0
Не понимаю почему не открывается детальное описание продукта
11th November, 11:51
4350
0
Нужно решить задачу по программированию на массивы
27th October, 18:01
4395
0
Метода Крамера С++
23rd October, 11:55
4309
0
помогите решить задачу на C++
22nd October, 17:31
4002
0
Помогите решить задачу на python с codeforces
22nd October, 11:11
4492
0
Python с нуля: полное руководство для начинающих
18th June, 13:58
2599
0
Задачка на многопоточность (.NET)?
Просмотров: 325
 
Ответов: 2
Какая проблема есть в коде? Как изменить код так, чтобы ее избежать?
using System;
using System.Threading;
namespace Mover
{
internal class Endpoint
{
public int Amount { get; set; }
}
internal class Program
{
private static void Main(string[] args)
{
var source = new Endpoint();
var target = new Endpoint();
var initialAmount = 1000000;
source.Amount = initialAmount;
var thread = new Thread(new ThreadStart(delegate
{
Transfer(source, target, initialAmount);
}));
thread.Start();
Transfer(target, source, initialAmount / 2);
thread.Join();
Console.Out.WriteLine("source.Amount = {0}", source.Amount);
Console.Out.WriteLine("target.Amount = {0}", target.Amount);
}
private static void Transfer(Endpoint source, Endpoint target, int count)
{
while (count-- > 0)
lock (target)
lock (source)
{
source.Amount--;
target.Amount++;
}
}
}
}
using System;
using System.Threading;
namespace Mover
{
internal class Endpoint
{
public int Amount { get; set; }
}
internal class Program
{
private static void Main(string[] args)
{
var source = new Endpoint();
var target = new Endpoint();
var initialAmount = 1000000;
source.Amount = initialAmount;
var thread = new Thread(new ThreadStart(delegate
{
Transfer(source, target, initialAmount);
}));
thread.Start();
Transfer(target, source, initialAmount / 2);
thread.Join();
Console.Out.WriteLine("source.Amount = {0}", source.Amount);
Console.Out.WriteLine("target.Amount = {0}", target.Amount);
}
private static void Transfer(Endpoint source, Endpoint target, int count)
{
while (count-- > 0)
lock (target)
lock (source)
{
source.Amount--;
target.Amount++;
}
}
}
}
Может получиться deadlock, если в основном потоке выполнится один из lock-ов, а потом управление отдастся другому потоку.
Избежать можно несколькими способами (тут задача очень мутная, цель непонятна, поэтому трудно сказать, как правильно)
1. сделать общий объект-lock
2. вынести lock в setter свойства Amount
3. лочиться на объекты в определённой последовательности; для этого, например, можно ввести id объектов
Предложу своё решение:
public class Endpoint
{
private Mutex _sendLock;
private Mutex _receiveLock;
private int _amount;
public int Amount
{
get
{
return _amount;
}
}
public Endpoint(int amount)
{
_sendLock = new Mutex();
_receiveLock = new Mutex();
_amount = amount;
}
public static void Transmit(Endpoint source, Endpoint destination, int amount)
{
WaitHandle[] transmittingPartiesHandles = new WaitHandle[] { source._sendLock, destination._receiveLock };
WaitHandle.WaitAll(transmittingPartiesHandles);
// Do smth.
Interlocked.Decrement(ref source._amount);
Interlocked.Increment(ref destination._amount);
source._sendLock.ReleaseMutex();
destination._receiveLock.ReleaseMutex();
}
}
public class Program
{
public static void Main(string[] args)
{
Endpoint detroit = new Endpoint(1000000);
Endpoint london = new Endpoint(0);
int amount = detroit.Amount;
Thread thread = new Thread(() => Transfer(detroit, london, amount));
thread.Start();
Transfer(london, detroit, amount / 2);
thread.Join();
Console.Out.WriteLine("source.Amount = {0}", detroit.Amount);
Console.Out.WriteLine("target.Amount = {0}", london.Amount);
Console.ReadLine();
}
private static void Transfer(Endpoint source, Endpoint target, int amount)
{
for (int i = 0; i < amount; i++)
{
Endpoint.Transmit(source, target, amount);
}
}
}
Во первых — делаем объект Endpoint потокобезопасным сам по себе. Ведь по сути изменять свойство Amount из внешнего кода нельзя.
Во вторых — избавляемся от локов на реальные объекты (это плохо, т.к. может существовать параллельно другой сценарий, в котором для совершенно других целей опять возьмётся лок на тот же объект Endpoint)
В третьих — выделяем процесс передачи в отдельный метод. Общий лок здесь не пройдет если будет настроенно сразу несколько передач в разных местах (будет много объектов «общей» блокировки для одной и той же пары конечных точек, из за чего возникнут проблемы).
public class Endpoint
{
private Mutex _sendLock;
private Mutex _receiveLock;
private int _amount;
public int Amount
{
get
{
return _amount;
}
}
public Endpoint(int amount)
{
_sendLock = new Mutex();
_receiveLock = new Mutex();
_amount = amount;
}
public static void Transmit(Endpoint source, Endpoint destination, int amount)
{
WaitHandle[] transmittingPartiesHandles = new WaitHandle[] { source._sendLock, destination._receiveLock };
WaitHandle.WaitAll(transmittingPartiesHandles);
// Do smth.
Interlocked.Decrement(ref source._amount);
Interlocked.Increment(ref destination._amount);
source._sendLock.ReleaseMutex();
destination._receiveLock.ReleaseMutex();
}
}
public class Program
{
public static void Main(string[] args)
{
Endpoint detroit = new Endpoint(1000000);
Endpoint london = new Endpoint(0);
int amount = detroit.Amount;
Thread thread = new Thread(() => Transfer(detroit, london, amount));
thread.Start();
Transfer(london, detroit, amount / 2);
thread.Join();
Console.Out.WriteLine("source.Amount = {0}", detroit.Amount);
Console.Out.WriteLine("target.Amount = {0}", london.Amount);
Console.ReadLine();
}
private static void Transfer(Endpoint source, Endpoint target, int amount)
{
for (int i = 0; i < amount; i++)
{
Endpoint.Transmit(source, target, amount);
}
}
}
Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться