PDA

View Full Version : [C#] Form principale si blocca mentre un altro Thread lavora da alcune ore


qwerty_race
21-01-2021, 08:17
Buongiorno,
ho fatto un programmino in "Win Form" con Visual Studio 2019.

Questo programmino posta sui gruppi Facebook tramite "Selenium + Chrome Driver", quindi apre un Browser e simula tutti i passaggi umani.

Ho fatto il "Form1" quindi l'interfaccia utente dove si realizza il post e si seleziona i gruppi dove postare,
attraverso un nuovo Thread avvio una nuova istanza di "Chrome Driver", quindi mi si apre il browser e inizia tutte le procedure, login, post, etc...

Avviandolo da Visual Studio per fare delle prove brevi su 5 gruppi con intervalli di 3 minuti tra i post tutto funziona correttamente.

Quando faccio delle prove più lunghe, tipo alcune ore e con intervalli da 30 minuti tra i post, lo ritrovo con questo errore:

Assistente al debug gestito 'ContextSwitchDeadlock' : 'CLR non è riuscito a passare dal contesto COM 0x7b3e00 al contesto COM 0x7b3d48 per 60 secondi. Probabilmente il thread a cui appartiene il contesto/apartment di destinazione è impegnato nell'attesa senza distribuzione o nell'elaborazione di un'operazione a esecuzione prolungata che non prevede la distribuzione di messaggi Windows. Questa situazione in genere ha un impatto negativo sulle prestazioni e può causare una mancata risposta dall'applicazione o un accumulo continuato nel tempo dell'utilizzo della memoria. Per evitare questo problema, è necessario che durante le operazioni a esecuzione prolungata tutti i thread di tipo apartment a thread singolo usino primitive di attesa con distribuzione (ad esempio CoWaitForMultipleHandles) e messaggi distribuiti in base a routine.'

Inoltre un altro dettaglio:
se entro nella cartella del progetto e lo avvio direttamente dall'exe, si apre il programma, lancio il ciclo dei post, poi clicco su un altra finestra in modo da fare perdere il "fuoco" al Form1, mi appare immediatamente la classica scritta di "frizz": (il programma non risponde).

Posto una breve porzione di codice per fare capire come gestisco il programmino:


static readonly WebReference.IBot wsIBot = new WebReference.IBot();
private readonly Browser oBrowser = new Browser(wsIBot );

public Form1()
{
InitializeComponent();

CheckForIllegalCrossThreadCalls = false;
}


[Obsolete]
private void BtnLogin_Click(object sender, EventArgs e)
{
th = new Thread(new ThreadStart(Login))
{
ApartmentState = ApartmentState.STA
};
}
th.Start();

private void Login
{
pbLogin.Minimum = 0;
pbLogin.Value = 0;
pbLogin.Step = 1;
pbLogin.Maximum = 8;

oBrowser.UrlLogin = UrlLogin;

oBrowser.OpenBrowser(PrograssBar1);
}




class Browser : File
{
WebReference.myRef wsRef;
private dynamic UrlLogin { get; set; } = null;
private ChromeDriverService service;
private string UrlLogin = "https://m.facebook.com/login";

public Browser(WebReference.myRef _wsRef)
{
wsIBot = _wsIBot;
}
}

public void OpenBrowser(ProgressBar _Pb)
{
service = ChromeDriverService.CreateDefaultService();

//Nascondo la Console
service.HideCommandPromptWindow = true;

//Disabilito:
ChromeOptions options = new ChromeOptions
{
PageLoadStrategy = PageLoadStrategy.Normal
};
options.AddArguments("--window-size=300,950");
options.AddArguments("--disable-extensions"); //extension
options.AddArguments("--disable-notifications"); //notification (PopUp "Notifiche" di Chrome)
options.AddArguments("--disable-application-cache"); //cache

driver = new ChromeDriver(service, options);

driver.Navigate().GoToUrl(UrlLogin.URL_NavigateTo.ToString());

_Pb.PerformStep();
}


Questo è una piccola parte del codice.
Mi chiedo:
- non sarà perchè cerco di gestire una ProgressBar su Form1 da un altro Thread?
---> oBrowser.OpenBrowser(PrograssBar1);

- oppure perche passo dei vlaori a delle Propertyes da Form1 alla classe Brovser su un nuovo thread?
---> oBrowser.UrlLogin = UrlLogin;

Non so da dove partire per vedere cosa sta succedendo, potreste darmi dei consigli per vedere se mi sblocco?

Vi ringrazio molto.

!fazz
22-01-2021, 09:58
il tuo errore è un classico errore di quando un metodo blocca la gui

ovvero hai un'operazione lunga che lavora sul thread di form1 e che quindi il thread risulta bloccato,

per risolvere devi prendere queste operazioni lunghe e metterle in background worker

per gestire la progress bar puoi usare il metodo già predisposto dall'oggetto (ProgressChanged)

ps se devi passare dati tra 2 thread la soluzione più facile è quella di usare una classe statica di appoggio

qwerty_race
22-01-2021, 10:09
Ho letto stamani della "background worker", speriamo di potersela fare.

Oltre alla guida ufficiale, mica hai un link per un esempio?

!fazz
22-01-2021, 10:21
Ho letto stamani della "background worker", speriamo di potersela fare.

Oltre alla guida ufficiale, mica hai un link per un esempio?

guarda è semplicissimo da usare

praticamente aggiungi l'oggetto backgroundworker al form esattamente come fosse una textbox
vai sulle props (f4)

doppio click su dowork e crea il metodo nomebgw_DoWork

li metti tutto il codice lento

quando devi richiamare il metodo basta che richiami nomebgw.RunWorkerAsync()

tutte le operazioni che invece reimpattano sulla gui (log progress bar ecc ecc ) devi richiamare dentro il bgw il metodo progressChanged dove le implementi

in alternativa se non vuoi usare il progress changed puoi richiamare dal backgroundworker un metodo del form mediante delegate