Skip to content

Conversation

@MikePuzanov
Copy link
Owner

No description provided.

namespace ThreadPool
{
/// <summary>
/// task interface
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Понятно что это интерфейс, но что это за интерфейс?

namespace ThreadPool
{
/// <summary>
/// task classs
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тоже не очень, даже более не очень, чем в предыдущем случае :)

/// <summary>
/// task classs
/// </summary>
public class MyTask<TResult> : IMyTask<TResult>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Его лучше сделать private- вложенным классом для MyThreadPool, чтобы у пользователей не было соблазна создавать его самому, вызывать Count и делать прочие ненужные вещи.

/// </summary>
public MyTask(Func<TResult> function)
{
_func = function ?? throw new ArgumentException();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Можно более специфично, ArgumentNullException(nameof(function));

_resultException = funcException;
}

lock (_locker)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

{
if (countThreads < 1)
{
throw new ArgumentException();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ArgumentOutOfRangeException(nameof(countThreads));

}

var task = new MyTask<TResult>(function);
_tasksQueue.Enqueue(task.Count);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не-а, если в этот момент сделают Shutdown и остановят все треды, задача добавится в очередь, но делать её будет некому. Получится задача-зомби --- она как бы жива, но не работает, и любой, кто вызовет её Result, задедлочится

lock (_lockObject)
{
_token.Cancel();
}

This comment was marked as resolved.

static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это не нужно, собирайте библиотеку просто

_threadPool.AddTask(() =>
{
Interlocked.Increment(ref number);
Thread.Sleep(3000);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Для этого есть всякие ManualResetEvent-ы, а то очень долго тесты исполняться будут

}
}

var task = new MyTask<TResult>(function, this);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Мм, и в этот момент происходит Shutdown, все потоки останавливаются, но мы уже не можем отменить добавление таска.

while (_continueWithTasksQueue.Count > 0)
{
var action = _continueWithTasksQueue.Dequeue();
lock (_locker)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Двойной lock на _locker? Если бы lock в C# был не рекурсивным, был бы дедлок :) Может, имелся в виду lockObject из тредпула? Но тогда доверили бы тредпулу им управлять

lock (_locker)
{
_myThreadPool._tasksQueue.Enqueue(action);
_waiterManual.Set();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Он и так выставлен тут уже. Или, опять-таки, имелся в виду флаг из тредпула?


var task = new MyTask<TNewResult>(() => func(Result), _myThreadPool);
_continueWithTasksQueue.Enqueue(task.Count);
if (IsCompleted)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Если IsCompleted, можно просто добавлять таск на тредпул, даже лочить никого не надо. О задачах из очереди позаботится Count. А то тут как-то очень много всего в критической секции.

Copy link
Collaborator

@yurii-litvinov yurii-litvinov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Надо ещё немного поправить


namespace ThreadPool
{
public class MyThreadPool

This comment was marked as resolved.

}
lock (_lockObject)
{
var task = new MyTask<TResult>(function, this);

This comment was marked as resolved.

Comment on lines 165 to 169
lock (_myThreadPool._lockObject)
{
_myThreadPool._tasksQueue.Enqueue(action);
_myThreadPool._waiterNewTask.Set();
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

{
throw new InvalidOperationException();
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Лучше сделайт требпулу приватный метод "Готов принимать новые задачи" и вызывайте его тут и в AddTask. А то это какое-то вмешательство в личную жизнь

}
}

var task = new MyTask<TNewResult>(() => func(Result), _myThreadPool);

This comment was marked as resolved.

Assert.AreEqual(16, task2.Result);
Assert.AreEqual(28, task3.Result);
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Надо бы ещё тесты на параллельные запросы к тредпулу (например, Shutdown и AddTask, что ничего не дедлочится)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Всё ещё нужно. Кажется у Вас методы самого тредпула из разных потоков нигде не вызываются.

Comment on lines 1 to 4
using System;

namespace ThreadPool
{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
using System;
namespace ThreadPool
{
namespace ThreadPool;
using System;

И дальше без лишнего отступа, здесь и ниже. C# 10 же.

Comment on lines 70 to 73
if (_token.IsCancellationRequested)
{
throw new InvalidOperationException();
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тут тоже можно было вызвать ReadyToAddNewTask()

{
_waiterNewTask.Set();
_waiterTaskDone.WaitOne();
_waiterTaskDone.Reset();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WaitOne же и так его опустит, это AutoResetEvent. По идее, этот Reset ничего не делает.

private readonly ConcurrentQueue<Action> _tasksQueue = new();
private readonly AutoResetEvent _waiterNewTask = new AutoResetEvent(false);
private readonly AutoResetEvent _waiterTaskDone = new AutoResetEvent(false);
private int _freeThreadsCount = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это должно быть volatile, потому что используется в Shutdown и рабочих потоках несинхронизированно. Есть риск дедлока в Shutdown, когда мы прочитали старое значение и пошли внутрь цикла, когда все потоки уже остановлены. Или в Shutdown Volatile.Read делать.

/// </summary>
public MyTask(Func<TResult> function, MyThreadPool myThreadPool)
{
_func = function ?? throw new ArgumentNullException(nameof(function));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Есть удобный метод ArgumentNullException.ThrowIfNull

Assert.AreEqual(16, task2.Result);
Assert.AreEqual(28, task3.Result);
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Всё ещё нужно. Кажется у Вас методы самого тредпула из разных потоков нигде не вызываются.

Copy link
Collaborator

@yurii-litvinov yurii-litvinov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Комментарии к коммитам ужасны. В остальном всё ок, зачтена.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants