Diskuze: Jak ukončit nečítání souborů?
V předchozím kvízu, Test znalostí C# .NET online, jsme si ověřili nabyté zkušenosti z kurzu.
Člen
Zobrazeno 23 zpráv z 23.
//= Settings::TRACKING_CODE_B ?> //= Settings::TRACKING_CODE ?>
V předchozím kvízu, Test znalostí C# .NET online, jsme si ověřili nabyté zkušenosti z kurzu.
jedna z možností by mohla být takto:
private boolean tlacitkoStornoStisknuto;
.....tlacitko_click.......(){
this.tlacitkoStornoStisknuto = true;
}
....nacti.....
{
while(this.tlacitkoStornoStisknuto != true)
{
//čtení
}
}
ani mi tak nejde o vlastní čtení souborů, to trvá asi tak 2-3 sec, ale o ověřování existence souborů a složek - to trvá někdy dost dlouho, protože jsou často v síti.
promiň, nějak teda nechápu dotaz , mám bohužel takovou blbou vlastnost že většinu věcí chápu jinak než ostatní
Moje chyba, jsem to dost podrobně nepopsal (nepovažoval jsem to za důležité). Jde mi o to, že někdy vypadne síť, a pak mi aplikace při načítání zamrzne (třeba na 2 minuty), než zjistí, že je soubor nenalezen. Já bych chtěl mít možnost tu aplikaci ukončit, protože jsem už při tom čekání zjistil, že nefunguje síť.
Pokud dobře vím, tak to nebude zrovna nejjednodušší.
Můžeš sem hodit těch pár řádků kódu ve kterejch kontroluješ
přítomnost souboru a načítáš ho?
Možná by to šlo zabalit do vlákna a pak na něj zavolat thread.Abort(), jenže to se už moc nedoporučuje a ani nevim jestli ho to chcípne hned nebo počká na ukončení rozdělané věci.
Přítomnost souboru testuji klasicky přes File.Existsts()
třeba tohle je první věc co načítám:
/// <summary>
/// načte položky ze souboru
/// </summary>
/// <param name="c"></param>
/// <param name="filename"></param>
/// <param name="silent">true=nebude zobrazovat chybové zprávy</param>
public static void LoadComboItems(this ComboBox c, string filename, bool silent)
{
if (c.Items.Count > 0) c.Items.Clear();
try
{
string[] lines = File.ReadAllLines(filename, Encoding.Default);
foreach (string s in lines) c.Items.Add(s);
}
catch (Exception e)
{
if (silent == false)
{
string msg = e.Message + "\nChyba pro soubor:\n" + filename;
MessageBox.Show(msg, "Chyba", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
Můžeš to dát do async metody, budeš mít statickou vlastnost bool která
bude říkat, jestli se má přerušit načítání. Tu vlastnost zkontroluješ
vždy před každým File.Exists, když bude true, nějak to načítání
dropneš, třeba returnem.
Asi to není nejlepší řešení, ale je nejjednodušší.
Můžeš se podívat sem: https://msdn.microsoft.com/…vs.110).aspx
ale to zase nemůžu kontrolovat při metodě File.Exists, myslím si, že z nějakého důvodu trvá dlouho pouze první zjištění, (nejspíš, že jde o podobné umístění), pak to jde rychle.
Takže tohle jsem vyplodil: tak by se to mělo správně dělat. Jen tam může být malá prodleva mezi kliknutím a zrušením.
private CancellationTokenSource tokenSource;
private bool? CheckFiles(IEnumerable<string> files, CancellationToken token)
{
try
{
foreach (var s in files)
{
token.ThrowIfCancellationRequested();
Thread.Sleep(400); //kontrola
}
return true;
}
catch (OperationCanceledException e)
{
MessageBox.Show("canceled");
return null;
}
}
private async void startBtn_click(object sender, EventArgs e)//start
{
tokenSource = new CancellationTokenSource();
List<string> fileList = new List<string>();
for (int i = 0; i < 20; i++)
{
fileList.Add("");
}
bool? loaded=await Task.Factory.StartNew(()=>CheckFiles(fileList,tokenSource.Token),tokenSource.Token);
MessageBox.Show(loaded.ToString());
}
private void stopBtn_click(object sender, EventArgs e)
{
tokenSource.Cancel();
}
Aha na async a await se
podívej, jsou to dost užitečné věci, hlavně pro formulářové aplikace.
CancellationToken je zkrátka tkaový blackbox, může ti být celkem jedno, jak
to funguje uvniř.
IEnumerable si můžeš představit jako list, ale je lepší dávat jako
parametr IEnumerable místo Listu, protože potom tam můžeš dát prakticky
jakoukoliv kolekci, třeba i pole a ne jen list. IEnumerable je také
užitečný jako návratový typ, protože se dá potom použít yield- další
užitečná věcička.
Teď jsem si ještě udělal takovou testovací aplikaci. Protože pořád tomu moc nerozumím, tak nevím, co mám špatně:
public partial class Form1 : Form
{
private CancellationTokenSource tokenSource;
public Form1()
{
InitializeComponent();
}
private bool? CheckFile(string filename, CancellationToken token)
{
try
{
//token.ThrowIfCancellationRequested(); - před fileexists to ingnoruje
bool exists = File.Exists(filename);
token.ThrowIfCancellationRequested();
return exists;
}
catch (OperationCanceledException)
{
return null;
}
}
private async void btStart_Click(object sender, EventArgs e)
{
tokenSource = new CancellationTokenSource();
bool? result = await Task.Factory.StartNew(() => CheckFile(@"\\192.128.14.2\test.file", tokenSource.Token), tokenSource.Token);
if (result == null) MessageBox.Show("Přerušeno uživatelem.");
else
{
string msg = (result.Value) ? "Soubor existuje" : "Soubor nenalezen!";
MessageBox.Show(msg);
}
}
private void btCancel_Click(object sender, EventArgs e)
{
if(tokenSource!=null) tokenSource.Cancel();
}
}
aplikaci lze sice bez problémů ukončit...
Jak bych to měl upravit, abych stiskem tlačítka ukončil metodu
CheckFile? Teď se mi to chová tak, že se CheckFile
vykonává do konce, ale pak vypíše přerušeno uživatelem.
To je přesně očekávané chování, předpokládal jsem kontrolování velkého množství malých souborů.
Ono právě nejdéle trvá ověření toho prvního souboru. Ty ostatní pokud jsou ve stejné složce, se ověří pak už rychle.
Obávám se, že to nejde File.Exists nejde přerušit v průběhu. Nešlo by
to ani kdyby to bylo ve vlastním vlákně.
Nic ale nebrání tomu, abys dělal jakože to už nepracuje, protože ta úloha
se stejně přeruší a vrátí null
asi takto:
private async void btStart_Click(object sender, EventArgs e)
{
tokenSource = new CancellationTokenSource();
bool? result = await Task.Factory.StartNew(() => CheckFile(@"\\192.128.14.2\test.file", tokenSource.Token), tokenSource.Token);
if (result == null) return;//zrušeno
//tady je už jisté, že operace nebyla zrušena
string msg = (result.Value) ? "Soubor existuje" : "Soubor nenalezen!";
MessageBox.Show(msg);
}
private void btCancel_Click(object sender, EventArgs e)
{
tokenSource?.Cancel();
MessageBox.Show("Přerušeno uživatelem.");
//prezentační logika po zrušení
}
Zbytek kódu samozřejmě zůstane. Opravdu to nejde lépe, nebylo by to thread-safe. Nešlo by to myslím ani ukončením procesu.
nebo nebudeš nic předstírat a přiznáš barvu: (asi čistější)
private CancellationTokenSource tokenSource;
private bool wasIoException=false;//došlo k vnitřní chybě?
private bool? CheckFile(List<string> filenames, CancellationToken token)//pokud je těch souborů víc,
//a chceš je kontrolovat najednou, mělo by to vypadat takto:
{
wasIoException=false;
try
{
foreach(var file in filenames)
{
token.ThrowIfCancellationRequested();//kontrolovat to až potom je úplně k ničemu
if(!File.Exists(file))
return false;
}
return true;
}
catch (OperationCanceledException)
{
return null;
}
catch(IOException)//nepřístupnost souboru
{
//tady nemůžeš k UI, musíš to ošetřit až dál
wasIoException=true;//proto si to zapíšeš
return null;
}
}
private async void btStart_Click(object sender, EventArgs e)
{
tokenSource = new CancellationTokenSource();
bool? result = await Task.Factory.StartNew(() => CheckFile(@"\\192.128.14.2\test.file", tokenSource.Token), tokenSource.Token);
if (result == null)
{
checking_canceled();
return;//zrušeno
}
//tady je už jisté, že operace nebyla zrušena
string msg = (result.Value) ? "Soubor existuje" : "Soubor nenalezen!";
MessageBox.Show(msg);
}
private void btCancel_Click(object sender, EventArgs e)
{
tokenSource?.Cancel();
//ukážeš něco jako rušení operace
}
private void checking_canceled()
{
//tady můžeš pracovat s UI
//logika, kdy je to už opravdu přerušené
//vypneš hlášku rušení operace
if(wasIoException)
{
//nastala chyba při kontrolování, ale operace nebyla zrušena
}
else
{
//operace byla zrušena uživatelem
}
}
Zobrazeno 23 zpráv z 23.