c# разделить массив на N массивов
Нужно разделить массив на N массивов равного размера. Как пытаюсь делать
private void start_ClickAsync(object sender, RoutedEventArgs e)
{
string[] arrString = new string[100];
for (int i = 0; i < arrString.Length; i++)
arrString[i] = "Индекс: [" + i + "] Значение:" + i * i;
int arrStringCountForThread = (arrString.Length / 10);
int nWorkerThreads =600;
int nCompletionThreads = 600;
ThreadPool.GetMaxThreads(out nWorkerThreads, out nCompletionThreads);
for (int i = 1; i < 9; i++)
{
string[] NEWarrString = new string[arrStringCountForThread];
// копируем из numbers с 2-го индекса 5 элементов
// и поместим их в массив numbers2, начиная с 0-го индекса
Array.Copy(arrString, (i*arrStringCountForThread), NEWarrString, 0, arrStringCountForThread);
// MessageBox.Show("Размер массива: " + arrStringCountForThread + " size new array: " + NEWarrString.Length);
//Thread.Sleep(500);
ThreadPool.QueueUserWorkItem(mythread1, NEWarrString);
// thread2.Start();
}
}
void mythread1(object state)
{
string[] daysOfWeek = (string[])state;
for (int i = 0; i < daysOfWeek.Length; i++)
{
Thread.Sleep(500);
string pool = Convert.ToString(Thread.CurrentThread.ManagedThreadId);
Dispatcher.BeginInvoke((Action)(() => this.result.Text +=daysOfWeek[i] + "\n"));
}
}
Получаю System.IndexOutOfRangeException: "Индекс находился вне границ массива."
Ответы (2 шт):
Реализация
static void SplitSpan<T>(ReadOnlySpan<T> buffer, Span<Range> ranges, out Range remaining)
{
remaining = default;
var segLen = buffer.Length / ranges.Length; // segment length
remaining = (segLen * ranges.Length)..;
var segOff = 0; // segment offset
for (var i = 0; i < ranges.Length; i++)
ranges[i] = segOff..(segOff += segLen);
}
Использование
static void Main()
{
var buf = new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // buffer
var rngs = new Range[3]; // ranges
SplitSpan<int>(buf, rngs, out var rem); // remaining
for (var i = 0; i < rngs.Length; i++)
PrintSpan<int>($"Subspan #{i + 1}: ", buf[rngs[i]]);
PrintSpan<int>("Remaining: ", buf[rem]);
}
// don't use in your project
static void PrintSpan<T>(string prefix, ReadOnlySpan<T> buffer)
{
Console.Write(prefix);
for (var i = 0; i < buffer.Length - 1; i++)
{
Console.Write(buffer[i].ToString());
Console.Write(' ');
}
if (!buffer.IsEmpty) Console.WriteLine(buffer[^1].ToString());
}
Вывод
Subspan #1: 0 1 2
Subspan #2: 3 4 5
Subspan #3: 6 7 8
Remaining: 9
Примечание. Комментарии, оставленные мной в коде, лучше убрать.
В методе SplitSpan сосредоточена вся реализация алгоритма. Аргумент buffer – входной массив (тип Span<T>/ReadOnlySpan<T> – это оптимизированная версия классических массивов), ranges – массив диапазонов итоговых подмассивов, remaining – то, что осталось. Чтобы получить подмассив нужно использовать вот такой код: buffer[ranges[i]], где i – индекс подмассива.
Меня количество диапазонов вы меняете размер итоговых подмассивов. Чтобы вычислить кол-во диапазонов, нужно длину массива поделить на длину подмассива.
Зачем нужны диапазоны? Почему сразу не преобразовать в массив? Дело в том, что если вы работаете с Span<T>/Memory<T>/T[], то в результате вы хотите получить объект этого типа, а если вы получаете его сразу, то пропадает совместимость между остальными. Span<T> – ссылка + длина, Memory<T> – ссылка на источник данных + стартовый индекс + длина, T[] – реализован внутри CLR. Поэтому диапазон является предпочтительной формой возврата, да и хранить его куда проще, ведь это 2 индекса, а в случае с возвратом готового подмассива вам придётся хранить в пустую скопированные данные.
У вас есть проблема с многопоточностью. Если где-то возникнет исключение, вы его не увидите, потому что никак не ожидаете завершения задач.
Так же, на данный момент C# позволяет достаточно просто работать с многопоточностью и асинхронностью с помощью async/await и Task.
я хочу собрать статистику по всем зарегистрированным сайтам в интернете. Собираюсь перебрать все возможные комбинации букв символов и цифр. Нужно получить максимум потоков. Что с этим справиться лучше?
Я бы сделал примерно так:
private async void start_ClickAsync(object sender, RoutedEventArgs e)
{
IProgress<string> progress = new Progress<string>(p => result.Text += p);
try
{
string[] arrString = new string[100];
for (int i = 0; i < arrString.Length; i++)
arrString[i] = "Индекс: [" + i + "] Значение:" + i * i;
int maxConcurrency = Environment.ProcessorCount * 2; // сколько одновременно может работать потоков
using (SemaphoreSlim semaphore = new SemaphoreSlim(maxConcurrency))
{
List<Task> tasks = new List<Task>();
foreach (string s in arrString)
{
await semaphore.WaitAsync(); // ждать, если максимальное количество уже работает, пока потоков не станет меньше
tasks.Add(Task.Run(() => ProcessString(s, progress, semaphore))); // запуск CPU-bound операции
}
await Task.WhenAll(tasks); // ждать завершения всех
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
private void ProcessString(string dayOfWeek, IProgress<string> progress, SemaphoreSlim semaphore)
{
try
{
progress.Report(dayOfWeek + Environment.NewLine);
}
finally
{
semaphore.Release();
}
}
Изучите разницу между CPU-зависимыми и I/O-зависимыми операциями здесь.
Если как вы указали в комментариях, у вас I/O операция, то есть обращение по сети, то она может выглядеть так
private static readonly HttpClient client = new HttpClient();
private async Task ProcessPage(string url, IProgress<string> progress, SemaphoreSlim semaphore)
{
try
{
string html = await client.GetStringAsync(url);
string result = DoSomething(html); // обработка полученной страницы
progress.Report(result);
}
finally
{
semaphore.Release();
}
}
using (SemaphoreSlim semaphore = new SemaphoreSlim(maxConcurrency))
{
List<Task> tasks = new List<Task>();
foreach (string url in urls)
{
await semaphore.WaitAsync();
tasks.Add(ProcessPage(url, progress, semaphore)); // запуск I/O-bound операции
}
await Task.WhenAll(tasks);
}
Обратите внимание, ответы от запущенных задач могут возвращаться не в том порядке, в каком задачи были запущены.